📚 Guía para crear tu EVA

Todo lo que necesitas: MOOC, Microlearning y B-Learning

🎯 ¿Qué es un MOOC?

Curso masivo, abierto y en línea. Sin límite de estudiantes, acceso libre, estructura modular.

  • Divide en 3-6 unidades temáticas
  • Cada unidad: 4-8 módulos
  • Combina: intro, video, lectura, foro, actividad, quiz
  • Videos recomendados: 5-12 minutos

🎬 Videos — Cómo agregarlos

⚠️ Importante: YouTube bloquea embeds en dominios externos. Esta plataforma muestra el thumbnail y un botón que abre el video en YouTube directamente (la mejor experiencia).

  • Sube tu video a YouTube (puede ser no listado)
  • Copia el link normal: youtube.com/watch?v=...
  • La plataforma muestra thumbnail + botón para ver en YouTube
  • Para videos que SÍ permiten embed (Vimeo, Drive, EducaPlay): funcionan directo en la página

🎮 EducaPlay — Cómo integrarlo

  • Crea tu actividad en educaplay.com
  • Ve a "Integrar" en tu actividad
  • Copia el código iframe completo
  • Pégalo en el campo de actividad — extrae el src automáticamente

⚡ Microlearning

Aprendizaje en pequeñas dosis (3-7 min). Ideal para móvil, alta retención.

  • 1 nodo = 1 solo concepto
  • Progresión visual por etapas
  • Retroalimentación inmediata
  • Gamificación: racha, desafíos
1

Descompón el tema

¿Qué es lo mínimo que el estudiante necesita entender en este paso?

2

Diseña la ruta

Simple → complejo. 3-6 nodos por etapa. Usa emojis para que sea visual.

3

Videos cortos

Máximo 5 minutos. YouTube Shorts, TikTok embed, grabaciones propias.

🏫 B-Learning

Combina presencial + virtual. 30-50% presencial, resto en línea.

  • Presencial: debates, prácticas, retroalimentación
  • En línea: lecturas, videos, tareas, quizzes
1

Define qué va dónde

Presencial = interacción. Online = contenido y evaluación.

2

Tablón de anuncios

Publica bienvenida, instrucciones y fechas antes de iniciar.

🌟 Principios del diseño instruccional

  • Coherencia: objetivos + actividades + evaluación alineados
  • Relevancia: conecta con la realidad del estudiante
  • Multimodalidad: texto, audio, video, interacción
  • Retroalimentación: inmediata y constructiva

📱 Herramientas gratuitas

  • EducaPlay · Genially · Wordwall · Kahoot · Padlet · Canva

📋 Taxonomía de Bloom

  • Recordar → Comprender → Aplicar → Analizar → Evaluar → Crear

👁 Vista previa del alumno

🌸
EVA Pedagogía

Elige tu sesión de equipo para comenzar a colaborar

⏳ Cargando sesiones...
👋 Bienvenida, Grecia
🎓 Alumna
Tu aula sin fronteras 🌸
Elige el tipo de curso que quieres explorar hoy
📚 MOOC
Pedagogía Digital
1 de 5 completados
UNIDAD 1 · Introducción
📋Bienvenida al cursointro
🎬¿Qué es la pedagogía?video
📄Lectura: Constructivismolectura
💬Foro de presentaciónforo
UNIDAD 2 · Diseño EVA
🎮Actividad EducaPlayactividad
🧩Quiz: Conceptos clavequiz
👈

Selecciona un módulo para comenzar

Microlearning
🌱 Etapa 1 — Fundamentos
🌿 Etapa 2 — Diseño
🔥
7 días
Racha

🎯 Desafíos del día

⚡ Completa 2 lecciones
1/2
⏱️ Estudia 10 min
3/10
🎯 80% en quiz
0/1

📖 Lección: ¿Qué es el aprendizaje significativo?

▶️Pega la URL del video
💡 YouTube: thumbnail + enlace a YouTube · Vimeo/Drive: se reproduce aquí

🧩 Mini-Quiz

🏫 B-Learning

🌸 Pedagogía & Tecnología Educativa · 2025

📣 Bienvenida al aula

Hola a todas, bienvenidas a nuestro aula de B-Learning. ¡Estoy emocionada de trabajar juntas! 🌸

📋
Tarea 1 · Secuencia didáctica
Publicada · Entrega: 25 may

Diseñen una secuencia con inicio, desarrollo y cierre.

🎮
Actividad EducaPlay
Interactiva · Siempre disponible

Completa la sopa de letras sobre pedagogía.

👩‍🎓 Compañeras
📅 Próximamente
Sesión presencial · 22 may
Entrega Tarea 1 · 25 may
Quiz en línea · 28 may
✓ Guardado
🔄 Actualizado
/* ══ FIREBASE ══ */ firebase.initializeApp({ apiKey:"AIzaSyDIucPhIp0gSLPcmCUTYrY7CJx5LYBzCQU", authDomain:"foro-ee8a5.firebaseapp.com", databaseURL:"https://foro-ee8a5-default-rtdb.firebaseio.com" }); const db=firebase.database(); const auth=firebase.auth(); /* Auth anónimo en background — no bloquea la UI */ window._authReady=false; auth.onAuthStateChanged(user=>{ window._authReady=!!user; }); auth.signInAnonymously().catch(err=>console.warn("Auth anónimo:",err.message)); /* ══ CONFIGURACIÓN DE SESIONES ══ */ /* Las sesiones se guardan en Firebase: /sesiones/{id} Estructura: { nombre, icon, color, pass, miembros:{}, modData:{}, progreso:{} } */ const TEACHER_PASS = "cuicalli666"; /* ══ LISTA COMPLETA DE ALUMNAS ══ */ const ALL_STUDENTS=[ {id:"grecia", nombre:"ALBINO VAZQUEZ GRECIA VALERI", firstName:"GRECIA"}, {id:"lluvia", nombre:"FELIX BAUTISTA LLUVIA GLORIA", firstName:"LLUVIA"}, {id:"diocelin", nombre:"FUENTES HERNANDEZ DIOCELIN MONSERRATH", firstName:"DIOCELIN"}, {id:"diana", nombre:"MARTINEZ GARCIA DIANA SHARON", firstName:"DIANA"}, {id:"perla", nombre:"RAMOS ROSARIO PERLA VALERIA", firstName:"PERLA"}, {id:"karen", nombre:"RANGEL PILLADO KAREN YOALI", firstName:"KAREN"}, {id:"brisa", nombre:"RANZAHUER VELAZQUEZ BRISA EDITH", firstName:"BRISA"}, {id:"josefina", nombre:"ROMAN GARCIA JOSEFINA", firstName:"JOSEFINA"}, {id:"marianne", nombre:"SANCHEZ ESCALANTE MARIANNE GISSELLE",firstName:"MARIANNE"}, {id:"brenda", nombre:"VELAZQUEZ HERNANDEZ BRENDA", firstName:"BRENDA"}, ]; /* Expanded icon set */ const ALL_ICONS=["🌹","💜","🌿","☁️","🍑","⭐","🌻","🦋","🌙","🔥","💎","🎯","📚","🎨","🌈", "🐱","🐶","🦊","🐸","🐼","🦁","🐯","🦄","🐙","🦋","🐳","🦜","🦩","🐺","🦌", "🍕","🎵","🎭","💃","🧠","🎪","🎠","🌮","🎸","🎨","🎬","🏄","🌺","🍀","💫", "🎀","🌸","✨","🎊","🎈","🏆","💡","🔮","🌊","🍭","🦚","🐬"]; /* Default sessions — each holds exactly 2 assigned students (set by teacher) */ const DEFAULT_SESSIONS = [ {id:"s1", nombre:"Equipo Rosas", icon:"🌹", color:"linear-gradient(135deg,#FFBCCA,#FF8FA3)", pass:"rosas123", design:"default", alumnos:["grecia","lluvia"]}, {id:"s2", nombre:"Equipo Lavanda", icon:"💜", color:"linear-gradient(135deg,#D4B8FF,#C77DFF)", pass:"lavanda123", design:"default", alumnos:["diocelin","diana"]}, {id:"s3", nombre:"Equipo Menta", icon:"🌿", color:"linear-gradient(135deg,#B8F0D8,#4DD9AC)", pass:"menta123", design:"default", alumnos:["perla","karen"]}, {id:"s4", nombre:"Equipo Cielo", icon:"☁️", color:"linear-gradient(135deg,#B8DCFF,#5BA3F5)", pass:"cielo123", design:"default", alumnos:["brisa","josefina"]}, {id:"s5", nombre:"Equipo Durazno", icon:"🍑", color:"linear-gradient(135deg,#FFD4B8,#FF9A76)", pass:"durazno123", design:"default", alumnos:["marianne","brenda"]}, {id:"s6", nombre:"Equipo Coral", icon:"🪸", color:"linear-gradient(135deg,#FFB5A7,#E87461)", pass:"coral123", design:"default", alumnos:[]}, ]; let currentSession=null; // {id, nombre, icon, color} let currentMember=null; // {name, joinedAt} let isTeacher=false; let currentModId=null, subModalTarget=null, addModType=null, quizAnswers={}; let autoSaveTimer=null; let presenceRef=null; const TYPE_ICONS={intro:"📋",video:"🎬",lectura:"📄",foro:"💬",actividad:"🎮",quiz:"🧩",texto:"📝"}; /* Datos de módulos (plantilla base, se sobreescribe con datos de Firebase) */ const BASE_MOD_DATA={ "mi-intro":{type:"intro",title:"Bienvenida al curso",url:"",desc:"Bienvenida al curso de Pedagogía Digital. Explora los recursos y construye tu EVA.",compTrigger:"ninguno",subBlocks:[]}, "mi-v1": {type:"video",title:"¿Qué es la pedagogía?",url:"",desc:"Mira el video y reflexiona sobre los conceptos.",compTrigger:"ver_video",subBlocks:[]}, "mi-lec1": {type:"lectura",title:"Lectura: Constructivismo",url:"",desc:"Lee el documento y toma nota de los puntos más importantes.",compTrigger:"apuntes",subBlocks:[]}, "mi-foro1":{type:"foro",title:"Foro de presentación",url:"",desc:"Comparte tu reflexión con el grupo.",compTrigger:"foro",subBlocks:[]}, "mi-act1": {type:"actividad",title:"Actividad EducaPlay",url:"https://es.educaplay.com/recursos/20009786-sopa_de_letras_tecnologia_educativa.html",desc:"Completa la sopa de letras.",compTrigger:"actividad",subBlocks:[]}, "mi-quiz1":{type:"quiz",title:"Quiz: Conceptos clave",url:"",desc:"Pon a prueba tu aprendizaje.",compTrigger:"quiz",subBlocks:[],quiz:{preguntas:[ {q:"¿Cuál teoría propuso David Ausubel?",opts:["Conductismo","Aprendizaje significativo","Inteligencias múltiples","Cognitivismo puro"],correcta:1}, {q:"¿Qué significa EVA?",opts:["Entorno Virtual de Aprendizaje","Evaluación de Alumnos","Educación Visual Activa","Espacio Virtual Académico"],correcta:0}, {q:"¿Cuál NO es un tipo de MOOC?",opts:["xMOOC","cMOOC","bMOOC","Examen presencial"],correcta:3}, {q:"La taxonomía de Bloom tiene:",opts:["4 niveles","5 niveles","6 niveles","7 niveles"],correcta:2}, {q:"El B-learning combina:",opts:["Solo clases online","Presencial y virtual","Solo presencial","Videos y libros"],correcta:1} ]}} }; let modData = JSON.parse(JSON.stringify(BASE_MOD_DATA)); /* ══════════════════════════════════════════════════════ SISTEMA DE GUARDADO — COMPLETAMENTE AISLADO POR SESION Cada sesión usa su propia ruta: /pedagogia/s/{sessionId}/ No existe ninguna ruta compartida entre sesiones. ══════════════════════════════════════════════════════ */ /* Devuelve la ruta raíz de esta sesión */ function sRef(path){ return "pedagogia/s/"+(currentSession?.id||"_")+"/"+path; } /* Snapshot completo del estado actual de la sesión */ function buildSessionSnapshot(){ // Capture ALL live DOM edits — don't rely on onblur having fired // 1. Capture from currently open module panel if(currentModId && modData[currentModId]){ const h3=document.getElementById("mod-main-h3"); const pp=document.getElementById("mod-main-p"); if(h3&&h3.textContent.trim()) modData[currentModId].title=h3.textContent.trim(); if(pp&&pp.textContent.trim()) modData[currentModId].desc=pp.textContent.trim(); // Capture URL inputs if(modData[currentModId].subBlocks){ modData[currentModId].subBlocks.forEach(sb=>{ const u=document.getElementById("svu-"+sb.uid)||document.getElementById("spu-"+sb.uid)||document.getElementById("sau-"+sb.uid); if(u&&u.value.trim()) sb.url=u.value.trim(); // Capture rich text notes const noteEl=document.getElementById("note-content-"+sb.uid); if(noteEl&&sb.type==="texto") sb.desc=noteEl.innerHTML||""; }); } // Capture objectives from DOM const objList=document.getElementById("obj-list-"+currentModId); if(objList){ const items=Array.from(objList.querySelectorAll("li")).map(li=>li.textContent.trim()).filter(t=>t); if(items.length>0) modData[currentModId].objectives=items; } } // 2. Capture mod-label edits from sidebar for ALL mods document.querySelectorAll(".mod-item").forEach(mi=>{ if(modData[mi.id]){ const label=mi.querySelector(".mod-label"); if(label&&label.textContent.trim()) modData[mi.id].title=label.textContent.trim(); } }); // 3. Serialize all modData const mods={}; Object.entries(modData).forEach(([k,v])=>{ if(!v) return; mods[k]={ type:v.type||"intro", title:v.title||"", url:v.url||"", desc:v.desc||"", compTrigger:v.compTrigger||"ninguno", objectives:(Array.isArray(v.objectives)&&v.objectives.length>0) ? [...v.objectives] : null, subBlocks:(v.subBlocks||[]).map(sb=>({ uid:sb.uid||"", type:sb.type||"", title:sb.title||"", url:sb.url||"", desc:sb.desc||"", isRichText:!!sb.isRichText, quiz:sb.quiz||null })), quiz:v.quiz||null }; }); // Progress const prog={}; document.querySelectorAll(".mod-item").forEach(m=>{ prog[m.id]=m.querySelector(".mod-dot")?.classList.contains("done")?"done":"pending"; }); // Full sidebar state: all visible units and modules in order const units=[]; document.querySelectorAll(".unit-group").forEach(ug=>{ units.push({id:ug.id, name:ug.querySelector(".unit-hdr-name")?.textContent.trim()||""}); }); const allMods=[]; document.querySelectorAll(".mod-item").forEach(mi=>{ const d=modData[mi.id]||BASE_MOD_DATA[mi.id]||{}; allMods.push({ id:mi.id, label:mi.querySelector(".mod-label")?.textContent.trim()||d.title||"", type:d.type||"intro", isDefault:!!BASE_MOD_DATA[mi.id], dotClass:mi.querySelector(".mod-dot")?.className||"mod-dot pending", dotText:mi.querySelector(".mod-dot")?.textContent.trim()||"○", unitId:mi.closest(".unit-group")?.id||null }); }); // Track which default items were deleted const allDefaultMods=Object.keys(BASE_MOD_DATA); const visibleMods=new Set(allMods.map(m=>m.id)); const deletedDefaultMods=allDefaultMods.filter(id=>!visibleMods.has(id)); const defaultUnitIds=["ug-1","ug-2"]; const visibleUnits=new Set(units.map(u=>u.id)); const deletedDefaultUnits=defaultUnitIds.filter(id=>!visibleUnits.has(id)); return { mods,prog,units,allMods,deletedDefaultMods,deletedDefaultUnits, courseName:document.getElementById("course-name")?.textContent||"", theme:document.body.className||"theme-1", design:document.body.dataset.design||"default", ts:Date.now(), by:currentMember?.name||"?" }; } /* Save full session snapshot to Firebase under /pedagogia/s/{sessionId}/ */ function saveSession(showBadge=true){ if(!currentSession||isTeacher) return Promise.resolve(); _ownSaveFlag=true; const snap=buildSessionSnapshot(); const clean=JSON.parse(JSON.stringify(snap,(key,val)=>val===undefined?null:val)); window._lastRemoteHash=JSON.stringify(clean); // Wait for auth if not ready yet (max 3s) const doWrite=()=>db.ref("pedagogia/s/"+currentSession.id).set(clean).then(()=>{ if(showBadge) showAutoSaveBadge(); }).catch(err=>{ console.warn("Save error:",err.message); try{localStorage.setItem("eva_s_"+currentSession.id,JSON.stringify(clean));}catch(e){} if(showBadge) showAutoSaveBadge(); }); if(window._authReady||auth.currentUser){ return doWrite(); } else { return new Promise(resolve=>{ const unsub=auth.onAuthStateChanged(user=>{ unsub(); doWrite().then(resolve); }); setTimeout(()=>{unsub();doWrite().then(resolve);},3000); }); } } /* Load and restore session from Firebase — FULL DOM RESET then apply saved data */ function loadSession(data){ // ── HARD RESET: restore DOM to factory defaults ── modData=JSON.parse(JSON.stringify(BASE_MOD_DATA)); // Remove custom units and reset defaults const unitsEl=document.getElementById("mooc-units"); if(unitsEl){ // Remove all custom unit-groups Array.from(unitsEl.querySelectorAll(".unit-group")).forEach(ug=>{ if(ug.id!=="ug-1"&&ug.id!=="ug-2") ug.remove(); }); // Restore ug-1 if missing if(!document.getElementById("ug-1")){ const div=document.createElement("div");div.className="unit-group";div.id="ug-1"; div.innerHTML=`
UNIDAD 1 · INTRODUCCIÓN
`; unitsEl.prepend(div); document.getElementById("dbtn-ug-1")?.addEventListener("click",()=>delEl("ug-1")); } // Restore ug-2 if missing if(!document.getElementById("ug-2")){ const div=document.createElement("div");div.className="unit-group";div.id="ug-2"; div.innerHTML=`
UNIDAD 2 · DISEÑO EVA
`; unitsEl.appendChild(div); document.getElementById("dbtn-ug-2")?.addEventListener("click",()=>delEl("ug-2")); } // Remove any lingering custom mod-items document.querySelectorAll(".mod-item").forEach(mi=>{ if(!BASE_MOD_DATA[mi.id]) mi.remove(); }); // Reset all default mod dots Object.keys(BASE_MOD_DATA).forEach(id=>{ const el=document.getElementById(id); if(el){const dot=el.querySelector(".mod-dot");if(dot){dot.className="mod-dot pending";dot.textContent="○";}} }); // Re-add any default mods that were removed const TAG={intro:"tag-intro",video:"tag-video",lectura:"tag-lectura",foro:"tag-foro",actividad:"tag-actividad",quiz:"tag-quiz"}; ["mi-intro","mi-v1","mi-lec1","mi-foro1","mi-act1","mi-quiz1"].forEach(id=>{ if(!document.getElementById(id)){ const d=BASE_MOD_DATA[id]; const btn=document.createElement("div");btn.className="mod-item";btn.id=id; const dbid="dbtn-"+id; btn.innerHTML=`
${TYPE_ICONS[d.type]||"📋"}${d.title}${d.type}`; btn.onclick=(e)=>{if(!e.target.classList.contains("del-btn"))showMod(id);}; const targetUnit=(id==="mi-act1"||id==="mi-quiz1")?document.getElementById("ug-2"):document.getElementById("ug-1"); (targetUnit||unitsEl).appendChild(btn); document.getElementById(dbid)?.addEventListener("click",e=>{e.stopPropagation();delMod(id);}); } }); } // Reset course name, theme, design, panel const cn=document.getElementById("course-name");if(cn)cn.textContent="Pedagogía Digital"; document.body.className="theme-1"; DESIGN_CLASSES.forEach(c=>document.body.classList.remove(c)); document.body.dataset.design="default"; if(window.updateCanvasDesign) window.updateCanvasDesign("default"); document.getElementById("mooc-panel").innerHTML=`
👈

Selecciona un módulo

`; currentModId=null; if(!data){ updateProgress(); setTimeout(()=>showMod("mi-intro"),80); return; } // ── Apply saved modData — full replace, not merge ── if(data.mods){ Object.entries(data.mods).forEach(([k,v])=>{ if(!v) return; // Full replace: start from BASE if default, then apply saved fields const base=BASE_MOD_DATA[k]?JSON.parse(JSON.stringify(BASE_MOD_DATA[k])):{}; modData[k]={ ...base, type:v.type||base.type||"intro", title:v.title||base.title||"", url:v.url||"", desc:v.desc||base.desc||"", compTrigger:v.compTrigger||base.compTrigger||"ninguno", objectives:Array.isArray(v.objectives)&&v.objectives.length>0 ? v.objectives : (base.objectives||null), subBlocks:v.subBlocks||[], quiz:v.quiz||base.quiz||null }; }); (data.deletedDefaultMods||[]).forEach(id=>delete modData[id]); } // ── Apply theme/design/courseName ── if(data.theme) document.body.className=data.theme; if(data.design&&data.design!=="default") applyDesign(data.design); if(data.courseName){const cn2=document.getElementById("course-name");if(cn2)cn2.textContent=data.courseName;} // ── Rebuild sidebar then show first module ── restoreFullSidebar({ units:data.units||[], allMods:data.allMods||[], deletedDefaultMods:data.deletedDefaultMods||[], deletedDefaultUnits:data.deletedDefaultUnits||[] }); // ── Restore progress dots ── if(data.prog){ Object.entries(data.prog).forEach(([id,status])=>{ if(status==="done"){const el=document.getElementById(id);const dot=el?.querySelector(".mod-dot");if(dot){dot.className="mod-dot done";dot.textContent="✓";}} }); } updateProgress(); // ── Show first available module ── const firstMod=document.querySelector(".mod-item"); if(firstMod) showMod(firstMod.id); else showMod("mi-intro"); // ── Show last saved info ── if(data.ts){ const badge=document.getElementById("autosave-badge"); if(badge){ badge.textContent="Última sesión: "+new Date(data.ts).toLocaleTimeString("es-MX",{hour:"2-digit",minute:"2-digit"})+" por "+(data.by||"?"); badge.style.opacity="1"; clearTimeout(badge._t); badge._t=setTimeout(()=>badge.style.opacity="0",6000); } } } /* Autosave debounce */ function triggerAutoSave(){ if(!currentSession||isTeacher) return; clearTimeout(autoSaveTimer); autoSaveTimer=setTimeout(()=>saveSession(true),1200); } function manualSave(){ if(!currentSession||isTeacher){toast("⚠️ No hay sesión activa");return;} const btn=document.getElementById("manual-save-btn"); if(btn){btn.textContent="⏳ Guardando...";btn.style.opacity=".7";} saveSession(false).then(()=>{ if(btn){btn.textContent="✓ Guardado";btn.style.opacity="1";setTimeout(()=>{btn.textContent="💾 Guardar";},2500);} toast("💾 Guardado · /pedagogia/s/"+currentSession.id); }).catch(()=>{ if(btn){btn.textContent="💾 Guardar";btn.style.opacity="1";} }); } function showAutoSaveBadge(){ const el=document.getElementById("autosave-badge"); if(!el) return; el.textContent="✓ Guardado · "+new Date().toLocaleTimeString("es-MX",{hour:"2-digit",minute:"2-digit"}); el.style.opacity="1"; el.style.color="#2A9D6A"; el.style.borderColor="rgba(77,217,172,.4)"; clearTimeout(el._t); el._t=setTimeout(()=>{el.style.opacity="0";},3000); } /* Realtime sync — only listens to THIS session's path */ let syncListener=null; let _ownSaveFlag=false; function startRealtimeSync(){ if(!currentSession) return; const path="pedagogia/s/"+currentSession.id; syncListener=db.ref(path).on("value",snap=>{ if(!snap.val()) return; if(_ownSaveFlag){_ownSaveFlag=false;return;} // skip own saves const remote=snap.val(); const remoteHash=JSON.stringify(remote); if(remoteHash===window._lastRemoteHash) return; // no change window._lastRemoteHash=remoteHash; // Smart update: apply collaborator changes without full DOM reset applyRemoteChanges(remote); showCollabToast(); }); } function startSync(){startRealtimeSync();} /* Apply remote collaborator changes without resetting the whole DOM */ function applyRemoteChanges(remote){ // Update modData from remote if(remote.mods){ Object.entries(remote.mods).forEach(([k,v])=>{ if(v) modData[k]=Object.assign(modData[k]||{},v); }); (remote.deletedDefaultMods||[]).forEach(id=>delete modData[id]); } // Refresh current module panel if data changed if(currentModId&&remote.mods&&remote.mods[currentModId]){ showMod(currentModId); } // Update progress dots if(remote.prog){ Object.entries(remote.prog).forEach(([id,status])=>{ const el=document.getElementById(id);const dot=el?.querySelector(".mod-dot"); if(!dot) return; if(status==="done"&&!dot.classList.contains("done")){dot.className="mod-dot done";dot.textContent="✓";} }); updateProgress(); } // Update sidebar for new units/mods added by collaborator if(remote.allMods||remote.units){ restoreFullSidebar({ units:remote.units||[],allMods:remote.allMods||[], deletedDefaultMods:remote.deletedDefaultMods||[], deletedDefaultUnits:remote.deletedDefaultUnits||[] }); } // Update course name, theme, design if changed if(remote.courseName){const cn=document.getElementById("course-name");if(cn&&cn.textContent!==remote.courseName)cn.textContent=remote.courseName;} if(remote.theme&&document.body.className!==remote.theme) document.body.className=remote.theme; if(remote.design&&remote.design!==document.body.dataset.design) applyDesign(remote.design); } /* ══ SESIONES LOBBY ══ */ function initLobby(){ // Show sessions immediately from DEFAULT_SESSIONS (no Firebase wait) const localSessions={}; DEFAULT_SESSIONS.forEach(s=>{localSessions[s.id]={nombre:s.nombre,icon:s.icon,color:s.color,pass:s.pass,alumnos:s.alumnos||[]};}); renderLobby(localSessions); try{ db.ref("pedagogia/sesiones_config").once("value").then(snap=>{ let sessions=snap.val(); if(!sessions){ // First use — initialize Firebase with all default sessions db.ref("pedagogia/sesiones_config").set(localSessions).catch(()=>{}); sessions=localSessions; } else { // Merge: add any new sessions from DEFAULT_SESSIONS that don't exist in Firebase yet let needsUpdate=false; DEFAULT_SESSIONS.forEach(s=>{ if(!sessions[s.id]){ sessions[s.id]={nombre:s.nombre,icon:s.icon,color:s.color,pass:s.pass,alumnos:s.alumnos||[]}; needsUpdate=true; } }); if(needsUpdate) db.ref("pedagogia/sesiones_config").update(sessions).catch(()=>{}); } renderLobby(sessions); }).catch(()=>{ console.log("Firebase no disponible — usando sesiones locales"); }); // Listen for real-time teacher updates db.ref("pedagogia/sesiones_config").on("value",snap=>{ if(snap.val()) renderLobby(snap.val()); }); }catch(e){ // Firebase no cargó — las sesiones locales ya están visibles console.log("Firebase no disponible"); } } function renderLobby(sessions){ const grid = document.getElementById("sessions-grid"); if(!grid) return; grid.innerHTML=""; Object.entries(sessions).forEach(([id, s])=>{ const card = document.createElement("div"); card.style.cssText=`background:white;border-radius:20px;padding:0;overflow:hidden;box-shadow:0 8px 28px rgba(180,140,200,.18);cursor:pointer;transition:transform .2s,box-shadow .2s;border:2px solid transparent;`; card.onmouseenter=()=>{card.style.transform="translateY(-4px)";card.style.boxShadow="0 14px 36px rgba(180,140,200,.28)";card.style.borderColor="rgba(255,143,163,.3)";}; card.onmouseleave=()=>{card.style.transform="";card.style.boxShadow="0 8px 28px rgba(180,140,200,.18)";card.style.borderColor="transparent";}; card.onclick=()=>openJoinModal(id,s); card.innerHTML=`
${s.icon||"🌸"}
${s.nombre||id}
2 alumnas · Clic para unirte
${(s.alumnos||[]).map(aid=>{const st=ALL_STUDENTS.find(s=>s.id===aid);return st?`
${st.nombre}
`:"";}).join("")} ${!(s.alumnos||[]).length?`Sin alumnas asignadas`:""}
`; grid.appendChild(card); // Update online indicators per assigned student try{ db.ref("pedagogia/presencia/"+id).on("value",pSnap=>{ const members=Object.values(pSnap.val()||{}).filter(m=>m?.online); const badge=document.getElementById("online-badge-"+id); if(badge){ if(members.length>0){badge.textContent="🟢 "+members.length+" en línea";badge.style.display="block";} else badge.style.display="none"; } // Update per-student dot (s.alumnos||[]).forEach(aid=>{ const st=ALL_STUDENTS.find(s=>s.id===aid); const dot=document.getElementById("dot-"+id+"-"+aid); if(dot&&st){ const isOnline=members.some(m=>m.name===st.nombre||m.name.includes(st.nombre.split(" ")[2]||st.nombre.split(" ")[0])); dot.style.background=isOnline?"#4DD9AC":"rgba(212,184,255,.2)"; } }); }); }catch(e){} }); } let pendingSessionId=null, pendingSession=null; /* ══ MODAL SESIÓN — muestra alumnas asignadas, login con Firebase Auth ══ */ function updateEmailPreview(){ const val=(document.getElementById("join-email-input")?.value||"").trim().toLowerCase().replace(/\s+/g,""); const preview=document.getElementById("join-email-preview"); if(preview) preview.textContent=val?(val.includes("@")?val:val+"@blackai.com"):""; } function openJoinModal(id,s){ pendingSessionId=id; pendingSession=s; document.getElementById("join-session-icon").textContent=s.icon; document.getElementById("join-session-name").textContent=s.nombre; document.getElementById("join-session-err").style.display="none"; document.getElementById("join-email-input").value=""; document.getElementById("join-pass-input").value=""; document.getElementById("join-email-preview").textContent=""; // Show only students assigned to this session const studentsDiv=document.getElementById("join-session-students"); const alumnos=s.alumnos||[]; if(studentsDiv){ if(alumnos.length>0){ studentsDiv.innerHTML=`

Selecciona tu nombre:

`; alumnos.forEach(aid=>{ // Try ALL_STUDENTS first, then Firebase const st=ALL_STUDENTS.find(x=>x.id===aid); const firstName=st?st.firstName:(aid.charAt(0).toUpperCase()+aid.slice(1)); const emailUser=st?(st.firstName||aid).toLowerCase():aid.toLowerCase(); const card=document.createElement("div"); card.style.cssText="display:flex;align-items:center;gap:10px;padding:9px 14px;border-radius:11px;border:1.5px solid rgba(255,143,163,.2);background:#FFFBF0;cursor:pointer;transition:all .2s;margin-bottom:5px"; card.onmouseenter=()=>{card.style.background="rgba(255,143,163,.08)";card.style.borderColor="var(--accent)";}; card.onmouseleave=()=>{card.style.background="#FFFBF0";card.style.borderColor="rgba(255,143,163,.2)";}; card.onclick=()=>{ document.getElementById("join-email-input").value=emailUser; updateEmailPreview(); document.getElementById("join-pass-input").focus(); // Highlight selected studentsDiv.querySelectorAll("[data-student]").forEach(c=>{c.style.borderColor="rgba(255,143,163,.2)";c.style.background="#FFFBF0";}); card.style.borderColor="var(--accent)";card.style.background="rgba(255,143,163,.12)"; }; card.dataset.student=aid; card.innerHTML=`
${firstName[0]}
${firstName}
${emailUser}@blackai.com
`; studentsDiv.appendChild(card); }); studentsDiv.style.display="block"; } else { studentsDiv.innerHTML=""; studentsDiv.style.display="none"; } } oM("modal-join-session"); setTimeout(()=>document.getElementById("join-email-input")?.focus(),300); } function doJoinSession(){ const emailRaw=(document.getElementById("join-email-input")?.value||"").trim().toLowerCase().replace(/\s+/g,""); const pass=document.getElementById("join-pass-input")?.value||""; const errEl=document.getElementById("join-session-err"); if(!emailRaw){errEl.style.display="block";errEl.textContent="⚠️ Selecciona tu nombre o escribe tu correo";return;} if(!pass){errEl.style.display="block";errEl.textContent="⚠️ Ingresa tu contraseña";return;} const email=emailRaw.includes("@")?emailRaw:emailRaw+"@blackai.com"; errEl.style.display="none"; // Sign in with Firebase Auth auth.signInWithEmailAndPassword(email,pass).then(cred=>{ const uid=cred.user.uid; // Load student profile from Firebase db.ref("pedagogia/alumnos/"+uid).once("value").then(snap=>{ const studentData=snap.val()||{}; const firstName=studentData.firstName||email.split("@")[0].toUpperCase(); const studentId=studentData.id||emailRaw; // Verify assigned to this session const alumnos=pendingSession.alumnos||[]; if(alumnos.length>0&&studentId&&!alumnos.includes(studentId)){ errEl.style.display="block";errEl.textContent="⚠️ No estás asignada a esta sesión."; auth.signOut();return; } cM("modal-join-session"); currentSession={id:pendingSessionId,...pendingSession}; currentMember={name:firstName,uid,joinedAt:Date.now()}; isTeacher=false; enterApp(); }); }).catch(e=>{ errEl.style.display="block"; errEl.textContent=(e.code==="auth/user-not-found"||e.code==="auth/wrong-password"||e.code==="auth/invalid-credential") ?"⚠️ Correo o contraseña incorrectos":"⚠️ "+e.message; }); } function doTeacherLogin(){ const email=document.getElementById("teacher-email-input").value.trim(); const pass=document.getElementById("teacher-pass-input").value; const err=document.getElementById("teacher-login-err"); if(!email||!pass){err.style.display="block";err.textContent="⚠️ Ingresa correo y contraseña";return;} err.style.display="none"; // Sign in with Firebase Auth auth.signInWithEmailAndPassword(email,pass).then(cred=>{ document.getElementById("teacher-login-overlay").style.display="none"; isTeacher=true; currentMember={name:email.split("@")[0],uid:cred.user.uid,joinedAt:Date.now()}; enterTeacherDashboard(); toast("¡Bienvenida, "+currentMember.name+"! 👩‍🏫"); }).catch(e=>{ err.style.display="block"; err.textContent=e.code==="auth/user-not-found"||e.code==="auth/wrong-password"||e.code==="auth/invalid-credential" ?"⚠️ Correo o contraseña incorrectos" :"⚠️ Error: "+e.message; }); } /* ══ PRESENCIA EN TIEMPO REAL ══ */ function setupPresence(){ if(!currentSession) return; const uid=currentMember.name.replace(/\s/g,"_")+"_"+Date.now(); presenceRef=db.ref("pedagogia/presencia/"+currentSession.id+"/"+uid); presenceRef.set({name:currentMember.name,online:true,joinedAt:Date.now()}); presenceRef.onDisconnect().remove(); // Show own badge with first name const studentRecord=ALL_STUDENTS.find(s=>s.nombre===currentMember.name); const myFirst=studentRecord?.firstName||currentMember.name.split(" ")[0]; const ownBadge=document.getElementById("own-presence-badge"); const ownName=document.getElementById("own-name-badge"); if(ownName) ownName.textContent=myFirst; if(ownBadge) ownBadge.style.display="flex"; // Listen — only this session's members db.ref("pedagogia/presencia/"+currentSession.id).on("value",snap=>{ const members=snap.val()||{}; const online=Object.values(members).filter(m=> m?.online && m.name && m.name!==currentMember.name && !("x" in m) ); const list=document.getElementById("collab-members-list"); if(list){ list.innerHTML=online.slice(0,2).map(m=>{ const sr=ALL_STUDENTS.find(s=>s.nombre===m.name); const firstName=sr?.firstName||m.name.split(" ")[0]; return `
${firstName}
`; }).join(""); } }); } /* ══ OLD SYNC — replaced by new system in buildSessionSnapshot block above ══ */ /* ══ RESTORE FULL SIDEBAR — handles deletions of default items ══ */ function restoreFullSidebar(state){ if(!state) {showMod("mi-intro");return;} const TAG={intro:"tag-intro",video:"tag-video",lectura:"tag-lectura",foro:"tag-foro",actividad:"tag-actividad",quiz:"tag-quiz",texto:"tag-intro"}; const unitsContainer=document.getElementById("mooc-units"); if(!unitsContainer) return; // Step 1: Remove default units that were deleted const deletedUnits=new Set(state.deletedDefaultUnits||[]); deletedUnits.forEach(uid=>{document.getElementById(uid)?.remove();}); // Step 2: Remove default mods that were deleted const deletedMods=new Set(state.deletedDefaultMods||[]); deletedMods.forEach(mid=>{document.getElementById(mid)?.remove();}); // Step 3: Restore unit names for surviving defaults (state.units||[]).forEach(u=>{ const existing=document.getElementById(u.id); if(existing){ const n=existing.querySelector(".unit-hdr-name"); if(n&&n.textContent.trim()!==u.name) n.textContent=u.name; } else { // Create new unit (custom, not default) const div=document.createElement("div");div.className="unit-group";div.id=u.id; const dbid="dbtn-"+u.id; div.innerHTML=`
${u.name}
`; unitsContainer.appendChild(div); document.getElementById(dbid)?.addEventListener("click",()=>delEl(u.id)); } }); // Step 4: Add any custom mods that aren't in the DOM yet (from allMods list) if(state.allMods){ state.allMods.forEach(em=>{ if(document.getElementById(em.id)) return; // already in DOM if(em.isDefault) return; // default items already handled above const btn=document.createElement("div"); btn.className="mod-item";btn.id=em.id; const dbid="dbtn-"+em.id; btn.innerHTML=`
${em.dotText||"○"}
${TYPE_ICONS[em.type]||"📋"}${em.label}${em.type}`; btn.onclick=(e)=>{if(!e.target.classList.contains("del-btn"))showMod(em.id);}; const target=em.unitId?document.getElementById(em.unitId):null; const units=document.querySelectorAll(".unit-group"); (target||units[units.length-1]||unitsContainer).appendChild(btn); document.getElementById(dbid)?.addEventListener("click",e=>{e.stopPropagation();delMod(em.id);}); }); } // Step 5: Show first available module const firstMod=document.querySelector(".mod-item"); if(firstMod) showMod(firstMod.id); else document.getElementById("mooc-panel").innerHTML=`

Agrega un módulo para comenzar

`; updateProgress(); } /* applySidebarState — still used for real-time sync updates */ function applySidebarState(state){ if(!state) return; // For real-time sync, use restoreFullSidebar to stay consistent restoreFullSidebar(state); } function deepMerge(base,override){ const result={...base}; Object.keys(override).forEach(k=>{ if(override[k]&&typeof override[k]==="object"&&!Array.isArray(override[k])){result[k]=deepMerge(base[k]||{},override[k]);} else{result[k]=override[k];} }); return result; } let collabToastTimer; function showCollabToast(){ const el=document.getElementById("collab-update-toast"); if(el){el.style.opacity="1";clearTimeout(collabToastTimer);collabToastTimer=setTimeout(()=>el.style.opacity="0",2500);} } /* Old buildSavePayload removed — use buildSessionSnapshot() instead */ /* ══ ENTRAR A LA APP ══ */ function enterApp(){ playSound(); document.getElementById("screen-login").style.display="none"; document.getElementById("screen-app").style.display="block"; document.getElementById("main-nav").style.display="flex"; // Get actual first name from student record const studentRecord=ALL_STUDENTS.find(s=>s.nombre===currentMember.name); const firstName=studentRecord?.firstName||currentMember.name.split(" ")[0]; document.getElementById("hero-name").textContent=firstName; document.getElementById("nav-uname").textContent=firstName; document.getElementById("nav-avatar").textContent=firstName[0].toUpperCase(); const b=document.getElementById("hero-badge"); b.textContent="\uD83C\uDFF7\uFE0F "+currentSession.nombre; b.className="role-badge alumna"; toast("\u00A1Bienvenida, "+firstName+"! \uD83C\uDF38 \u2014 "+currentSession.nombre); buildClassmates(); setupPresence(); const sb=document.getElementById("manual-save-btn"); if(sb) sb.style.display="inline-block"; // Load from isolated session path /pedagogia/s/{sessionId}/ db.ref("pedagogia/s/"+currentSession.id).once("value").then(snap=>{ const data=snap.val(); // Store the hash of what we just loaded so realtime sync doesn't overwrite it window._lastRemoteHash=JSON.stringify(data); loadSession(data); // Start realtime sync AFTER load settles setTimeout(()=>startRealtimeSync(),800); }).catch(()=>{ try{ const backup=localStorage.getItem("eva_s_"+currentSession.id); if(backup) loadSession(JSON.parse(backup)); else loadSession(null); }catch(e){loadSession(null);} setTimeout(()=>startRealtimeSync(),800); }); } function logout(){ if(presenceRef) presenceRef.remove(); if(syncListener&¤tSession){ try{db.ref("pedagogia/s/"+currentSession.id).off("value",syncListener);}catch(e){} syncListener=null; } currentSession=null;currentMember=null;isTeacher=false;currentModId=null; modData=JSON.parse(JSON.stringify(BASE_MOD_DATA)); window._lastRemoteHash=null; window._lastSidebarHash=null; const saveBtn=document.getElementById("manual-save-btn"); if(saveBtn) saveBtn.style.display="none"; document.getElementById("screen-app").style.display="none"; document.getElementById("screen-teacher").style.display="none"; document.getElementById("main-nav").style.display="none"; document.getElementById("screen-login").style.display="flex"; initLobby(); } /* ══ PANEL PROFESORA ══ */ function enterTeacherDashboard(){ playSound(); document.getElementById("screen-login").style.display="none"; document.getElementById("screen-app").style.display="none"; document.getElementById("screen-teacher").style.display="block"; document.getElementById("main-nav").style.display="flex"; const teacherName=auth.currentUser?.email?.split("@")[0]||"Docente"; const displayName=teacherName.charAt(0).toUpperCase()+teacherName.slice(1); document.getElementById("nav-uname").textContent=displayName; document.getElementById("nav-avatar").textContent=displayName[0].toUpperCase(); toast("¡Bienvenida, "+displayName+"! 👩‍🏫"); loadTeacherDashboard(); loadSessionEditor(); loadStudentManager(); } function loadTeacherDashboard(){ const container=document.getElementById("teacher-sessions-container"); container.innerHTML=`
⏳ Cargando sesiones...
`; db.ref("pedagogia/sesiones_config").once("value").then(cfgSnap=>{ const configs=cfgSnap.val()||{}; // Si no hay configs, inicializar if(!Object.keys(configs).length){ const init={}; DEFAULT_SESSIONS.forEach(s=>{init[s.id]={nombre:s.nombre,icon:s.icon,color:s.color,pass:s.pass,alumnos:s.alumnos||[]};}); db.ref("pedagogia/sesiones_config").set(init); loadTeacherDashboard(); return; } container.innerHTML=""; Object.entries(configs).forEach(([sid,s])=>{ db.ref("pedagogia/s/"+sid).once("value").then(dataSnap=>{ const data=dataSnap.val()||{}; const prog=data.prog||{}; const mData=data.mods||{}; const totalMods=Object.keys(BASE_MOD_DATA).length; const doneMods=Object.values(prog).filter(v=>v==="done").length; const pct=totalMods>0?Math.round(doneMods/totalMods*100):0; // Escuchar presencia db.ref("pedagogia/presencia/"+sid).once("value").then(pSnap=>{ const pMembers=Object.values(pSnap.val()||{}).filter(m=>m?.online); const card=document.createElement("div"); card.style.cssText="background:var(--card-bg);border-radius:var(--radius);box-shadow:0 6px 24px var(--shadow);overflow:hidden;margin-bottom:20px;"; card.innerHTML=`
${s.icon}
${pMembers.length>0?`🟢 ${pMembers.map(m=>m.name).join(", ")} en línea`:"Sin miembros activos"}
${pct}% completado
🔑 Contraseña: Comparte esta contraseña con el equipo

📊 Avance por módulos (predeterminados + creados por el equipo)

${(()=>{ // Show BASE modules + any custom modules saved in Firebase const allMods={...BASE_MOD_DATA,...(mData||{})}; return Object.entries(allMods).map(([mid,m])=>{ const isDone=prog[mid]==="done"; const mInfo=mData[mid]||m; const isCustom=!BASE_MOD_DATA[mid]; return `
${TYPE_ICONS[mInfo.type||m.type||"intro"]||"📋"} ${mInfo.title||m.title||mid} ${isCustom?`nuevo`:""} ${isDone?``:`Pendiente`}
${mInfo.url?`
🔗 ${mInfo.url}
`:""}
`; }).join(""); })()}
`; container.appendChild(card); // Render student assigner const assignerEl=document.getElementById("assigner-"+sid); if(assignerEl) renderStudentAssigner(sid,s.alumnos||[],assignerEl); // Cargar calificaciones y feedbacks guardados db.ref("pedagogia/calificaciones/"+sid).once("value").then(gSnap=>{ if(gSnap.val()){ Object.entries(gSnap.val()).forEach(([mid,v])=>{ const gs=document.getElementById("grade-"+sid+"-"+mid); const fb=document.getElementById("fb-"+sid+"-"+mid); if(gs&&v.grade) gs.value=v.grade; if(fb&&v.feedback) fb.value=v.feedback; }); } }); }); }); }); }); } function showActivityLog(){ const panel=document.getElementById("activity-log-panel"); panel.style.display="block"; panel.scrollIntoView({behavior:"smooth"}); const list=document.getElementById("activity-log-list"); list.innerHTML=`

⏳ Cargando...

`; try{ db.ref("pedagogia/logs").once("value").then(snap=>{ const data=snap.val()||{}; // Collect all logs from all sessions const allLogs=[]; Object.entries(data).forEach(([sid,entries])=>{ Object.values(entries).forEach(entry=>{ allLogs.push({...entry,sid}); }); }); allLogs.sort((a,b)=>(b.ts||0)-(a.ts||0)); if(allLogs.length===0){list.innerHTML=`

No hay actividad registrada aún.

`;return;} list.innerHTML=allLogs.slice(0,100).map(e=>{ const d=new Date(e.ts||0); const timeStr=d.toLocaleDateString("es-MX",{day:"2-digit",month:"short"})+" "+d.toLocaleTimeString("es-MX",{hour:"2-digit",minute:"2-digit"}); return `
${(e.by||"?")[0].toUpperCase()}
${e.by||"?"} · ${e.session||e.sid}
${e.action||""}
${timeStr}
`; }).join(""); }).catch(()=>{list.innerHTML=`

⚠️ No se pudo cargar el registro

`;}); }catch(e){list.innerHTML=`

⚠️ Firebase no disponible

`;} } function loadSessionEditor(){ const grid=document.getElementById("session-editor-grid"); if(!grid) return; const COLORS=[ ["linear-gradient(135deg,#FFBCCA,#FF8FA3)","Rosa"], ["linear-gradient(135deg,#D4B8FF,#C77DFF)","Lavanda"], ["linear-gradient(135deg,#B8F0D8,#4DD9AC)","Menta"], ["linear-gradient(135deg,#B8DCFF,#5BA3F5)","Cielo"], ["linear-gradient(135deg,#FFD4B8,#FF9A76)","Durazno"], ["linear-gradient(135deg,#FFF3B0,#F7C948)","Limón"], ["linear-gradient(135deg,#FFBCCA,#D4B8FF)","Rosa-Lavanda"], ["linear-gradient(135deg,#B8F0D8,#B8DCFF)","Menta-Cielo"], ["linear-gradient(135deg,#FF6B35,#F7C948)","Naranja"], ["linear-gradient(135deg,#7C3AED,#EC4899)","Violeta"], ["linear-gradient(135deg,#E11D48,#7C3AED)","Rojo"], ["linear-gradient(135deg,#0891B2,#6366F1)","Azul"], ["linear-gradient(135deg,#DB2777,#F97316)","Fucsia"], ]; try{ db.ref("pedagogia/sesiones_config").once("value").then(snap=>{ const sessions=snap.val()||{}; grid.innerHTML=""; Object.entries(sessions).forEach(([sid,s])=>{ const card=document.createElement("div"); card.style.cssText="background:white;border-radius:14px;padding:18px;border:1.5px solid rgba(212,184,255,.2);"; card.innerHTML=`
${s.icon}

🎭 Icono de la sesión:

${ALL_ICONS.map(ic=>``).join("")}

🎨 Color del equipo:

${COLORS.map(([col,name])=>``).join("")}
`; grid.appendChild(card); }); }); }catch(e){} } function updateSessionIcon(sid,icon,btn){ db.ref("pedagogia/sesiones_config/"+sid+"/icon").set(icon); const card=btn.closest("div[style*='background:white']"); const preview=card?.querySelector("div[style*='border-radius:10px']"); if(preview) preview.textContent=icon; card?.querySelectorAll("button[onclick*='updateSessionIcon']").forEach(b=>{ b.style.borderColor="transparent";b.style.background="white"; }); btn.style.borderColor="var(--accent)";btn.style.background="rgba(255,143,163,.1)"; toast("✨ Icono actualizado"); } function updateSessionColor(sid,color,btn){ db.ref("pedagogia/sesiones_config/"+sid+"/color").set(color); const card=btn.closest("div[style*='background:white']"); const preview=card?.querySelector("div[style*='border-radius:10px']"); if(preview) preview.style.background=color; card?.querySelectorAll("button[onclick*='updateSessionColor']").forEach(b=>b.style.borderColor="white"); btn.style.borderColor="#4DD9AC"; toast("🎨 Color actualizado"); } function updateSessionDesign(sid,designId,btn){ db.ref("pedagogia/sesiones_config/"+sid+"/design").set(designId); // Also save to session data so students get it on sync db.ref("pedagogia/s/"+sid+"/design").set(designId); const card=btn.closest("div[style*='background:white']"); card?.querySelectorAll("button[onclick*='updateSessionDesign']").forEach(b=>{b.style.borderColor="rgba(212,184,255,.2)";b.style.background="white";}); btn.style.borderColor="var(--accent)";btn.style.background="rgba(255,143,163,.1)"; toast("🎨 Diseño actualizado — los alumnos lo verán al actualizar"); } function openNewSessionModal(){ document.getElementById("ns-nombre").value=""; document.getElementById("ns-pass").value=""; document.getElementById("ns-icon-val").value="⭐"; document.getElementById("ns-color-val").value="linear-gradient(135deg,#FFBCCA,#FF8FA3)"; document.getElementById("ns-error").style.display="none"; // Populate icon picker const iconPicker=document.getElementById("ns-icon-picker"); if(iconPicker&&!iconPicker.hasChildNodes()){ iconPicker.innerHTML=ALL_ICONS.map(ic=> `` ).join(""); } else if(iconPicker){ iconPicker.querySelectorAll("button").forEach(b=>b.style.borderColor="transparent"); } // Populate color picker const NS_COLORS=[ ["linear-gradient(135deg,#FFBCCA,#FF8FA3)","Rosa"], ["linear-gradient(135deg,#D4B8FF,#C77DFF)","Lavanda"], ["linear-gradient(135deg,#B8F0D8,#4DD9AC)","Menta"], ["linear-gradient(135deg,#B8DCFF,#5BA3F5)","Cielo"], ["linear-gradient(135deg,#FFD4B8,#FF9A76)","Durazno"], ["linear-gradient(135deg,#FFB5A7,#E87461)","Coral"], ["linear-gradient(135deg,#FFF3B0,#F7C948)","Limón"], ["linear-gradient(135deg,#C8B8FF,#7C3AED)","Violeta"], ]; const colorPicker=document.getElementById("ns-color-picker"); if(colorPicker&&!colorPicker.hasChildNodes()){ colorPicker.innerHTML=NS_COLORS.map(([col,name])=> `` ).join(""); } document.getElementById("modal-new-session").style.display="flex"; document.getElementById("ns-nombre").focus(); } function selectNewSessionIcon(icon,btn){ document.getElementById("ns-icon-val").value=icon; document.querySelectorAll("#ns-icon-picker button").forEach(b=>b.style.borderColor="transparent"); btn.style.borderColor="var(--accent)"; } function selectNewSessionColor(color,btn){ document.getElementById("ns-color-val").value=color; btn.closest("div").querySelectorAll("button").forEach(b=>b.style.borderWidth="3px"); btn.style.borderWidth="4px"; btn.style.borderColor="#4DD9AC"; } function createNewSession(){ const nombre=document.getElementById("ns-nombre").value.trim(); const pass=document.getElementById("ns-pass").value.trim(); const icon=document.getElementById("ns-icon-val").value||"⭐"; const color=document.getElementById("ns-color-val").value||"linear-gradient(135deg,#FFBCCA,#FF8FA3)"; const errEl=document.getElementById("ns-error"); if(!nombre){errEl.textContent="⚠️ Escribe un nombre";errEl.style.display="block";return;} if(!pass){errEl.textContent="⚠️ Escribe una contraseña";errEl.style.display="block";return;} // Generate unique session ID const newId="s"+Date.now(); const newSession={nombre,pass,icon,color,design:"default",alumnos:[]}; // Save to Firebase db.ref("pedagogia/sesiones_config/"+newId).set(newSession).then(()=>{ document.getElementById("modal-new-session").style.display="none"; toast("✨ Sesión '"+nombre+"' creada — ID: "+newId); loadSessionEditor(); loadTeacherDashboard(); }).catch(e=>{ errEl.textContent="⚠️ Error al guardar: "+e.message; errEl.style.display="block"; }); } /* ══ GESTIÓN DE ALUMNAS ══ */ function loadStudentManager(){ const list=document.getElementById("student-manager-list"); if(!list) return; list.innerHTML=`
⏳ Cargando...
`; db.ref("pedagogia/alumnos").once("value").then(snap=>{ const alumnos=snap.val()||{}; const entries=Object.entries(alumnos); if(!entries.length){ list.innerHTML=`
No hay alumnas registradas aún.
Haz clic en + Nueva alumna para comenzar.
`; return; } list.innerHTML=""; entries.forEach(([uid,a])=>{ const card=document.createElement("div"); card.style.cssText="background:white;border-radius:14px;padding:14px 16px;border:1.5px solid rgba(212,184,255,.2);display:flex;align-items:center;gap:12px;"; card.innerHTML=`
${(a.firstName||a.nombre||"?")[0]}
${a.firstName||a.nombre||"Sin nombre"}
${a.email||uid}
`; list.appendChild(card); }); }).catch(()=>{ list.innerHTML=`
⚠️ Error al cargar alumnas
`; }); } function autoFillStudentEmail(){ const firstName=(document.getElementById("st-firstname")?.value||"").trim().toLowerCase().replace(/\s+/g,""); const emailEl=document.getElementById("st-email"); if(emailEl&&firstName&&!emailEl.dataset.manualEdit) emailEl.value=firstName; } function genPassword(){ const chars="abcdefghjkmnpqrstuvwxyz23456789"; let pass="";for(let i=0;i<8;i++) pass+=chars[Math.floor(Math.random()*chars.length)]; const inp=document.getElementById("st-pass"); if(inp){inp.value=pass;inp.type="text";} } function openCreateStudentModal(){ document.getElementById("st-uid").value=""; document.getElementById("st-nombre").value=""; document.getElementById("st-firstname").value=""; document.getElementById("st-email").value=""; document.getElementById("st-email").dataset.manualEdit=""; document.getElementById("st-pass").value=""; document.getElementById("st-error").style.display="none"; document.getElementById("st-success").style.display="none"; document.getElementById("modal-student-title").textContent="✨ Nueva alumna"; document.getElementById("st-save-btn").textContent="Crear alumna ✨"; document.getElementById("modal-student").style.display="flex"; setTimeout(()=>document.getElementById("st-nombre").focus(),200); } function openEditStudentModal(uid){ db.ref("pedagogia/alumnos/"+uid).once("value").then(snap=>{ const a=snap.val()||{}; document.getElementById("st-uid").value=uid; document.getElementById("st-nombre").value=a.nombre||""; document.getElementById("st-firstname").value=a.firstName||""; document.getElementById("st-email").value=(a.email||"").replace("@blackai.com",""); document.getElementById("st-email").dataset.manualEdit="1"; document.getElementById("st-pass").value=""; document.getElementById("st-error").style.display="none"; document.getElementById("st-success").style.display="none"; document.getElementById("modal-student-title").textContent="✏️ Editar alumna"; document.getElementById("st-save-btn").textContent="Guardar cambios"; document.getElementById("modal-student").style.display="flex"; }); } function saveStudent(){ const uid=document.getElementById("st-uid").value; const nombre=(document.getElementById("st-nombre").value||"").trim().toUpperCase(); const firstName=(document.getElementById("st-firstname").value||"").trim().toUpperCase(); const emailUser=(document.getElementById("st-email").value||"").trim().toLowerCase().replace(/\s+/g,""); const pass=(document.getElementById("st-pass").value||"").trim(); const errEl=document.getElementById("st-error"); const sucEl=document.getElementById("st-success"); errEl.style.display="none";sucEl.style.display="none"; if(!nombre){errEl.textContent="⚠️ Ingresa el nombre completo";errEl.style.display="block";return;} if(!firstName){errEl.textContent="⚠️ Ingresa el nombre de pila";errEl.style.display="block";return;} if(!emailUser){errEl.textContent="⚠️ Ingresa el correo";errEl.style.display="block";return;} const email=emailUser.includes("@")?emailUser:emailUser+"@blackai.com"; const btn=document.getElementById("st-save-btn"); btn.textContent="⏳ Guardando...";btn.disabled=true; if(!uid){ // CREATE — use secondary Firebase app so teacher stays signed in if(!pass||pass.length<6){errEl.textContent="⚠️ La contraseña debe tener al menos 6 caracteres";errEl.style.display="block";btn.textContent="Crear alumna ✨";btn.disabled=false;return;} const teacherUser=auth.currentUser; // Create secondary app to avoid signing out teacher const secondaryApp=firebase.apps.find(a=>a.name==="secondary")||firebase.initializeApp({ apiKey:"AIzaSyDIucPhIp0gSLPcmCUTYrY7CJx5LYBzCQU", authDomain:"foro-ee8a5.firebaseapp.com" },"secondary"); const secondaryAuth=secondaryApp.auth(); secondaryAuth.createUserWithEmailAndPassword(email,pass).then(cred=>{ const newUid=cred.user.uid; secondaryAuth.signOut(); const studentRecord={nombre,firstName,email,id:emailUser.split("@")[0],createdAt:Date.now(),createdBy:teacherUser?.email||"docente"}; return db.ref("pedagogia/alumnos/"+newUid).set(studentRecord); }).then(()=>{ sucEl.textContent="✅ Cuenta creada: "+email+" · Contraseña: "+pass;sucEl.style.display="block"; btn.textContent="Crear alumna ✨";btn.disabled=false; document.getElementById("st-nombre").value=""; document.getElementById("st-firstname").value=""; document.getElementById("st-email").value=""; document.getElementById("st-pass").value=""; loadStudentManager(); }).catch(e=>{ errEl.textContent=e.code==="auth/email-already-in-use"?"⚠️ Ese correo ya existe":"⚠️ "+e.message; errEl.style.display="block";btn.textContent="Crear alumna ✨";btn.disabled=false; }); } else { // UPDATE existing student record const updates={nombre,firstName,email}; db.ref("pedagogia/alumnos/"+uid).update(updates).then(()=>{ if(pass&&pass.length>=6){ // Update password — requires re-auth, skip for now and show note sucEl.textContent="✅ Datos actualizados. Para cambiar contraseña, usa Firebase Console por ahora."; } else { sucEl.textContent="✅ Alumna actualizada correctamente"; } sucEl.style.display="block";btn.textContent="Guardar cambios";btn.disabled=false; loadStudentManager(); }).catch(e=>{ errEl.textContent="⚠️ "+e.message;errEl.style.display="block";btn.textContent="Guardar cambios";btn.disabled=false; }); } } function deleteStudent(uid,email){ if(!confirm("¿Eliminar a "+email+"? Esto eliminará su perfil de la base de datos (su cuenta de Firebase Auth permanece).")) return; db.ref("pedagogia/alumnos/"+uid).remove().then(()=>{ toast("🗑 Alumna eliminada");loadStudentManager(); }); } function updateSessionName(sid,val){ clearTimeout(window["nt_"+sid]); window["nt_"+sid]=setTimeout(()=>db.ref("pedagogia/sesiones_config/"+sid+"/nombre").set(val),800); } function updateSessionPass(sid,val){ clearTimeout(window["pt_"+sid]); window["pt_"+sid]=setTimeout(()=>db.ref("pedagogia/sesiones_config/"+sid+"/pass").set(val),800); } /* ══ STUDENT ASSIGNMENT ══ */ function saveStudentAssignment(sid,slot,studentId){ // Read current alumnos, update slot (0 or 1), save db.ref("pedagogia/sesiones_config/"+sid+"/alumnos").once("value").then(snap=>{ const current=snap.val()||[null,null]; const updated=[...current]; while(updated.length<2) updated.push(null); updated[slot]=studentId||null; db.ref("pedagogia/sesiones_config/"+sid+"/alumnos").set(updated.filter(Boolean)); toast("✅ Asignación guardada"); }).catch(()=>{ db.ref("pedagogia/sesiones_config/"+sid+"/alumnos/"+slot).set(studentId||null); toast("✅ Guardado"); }); } function renderStudentAssigner(sid,alumnos,container){ const opts=``+ ALL_STUDENTS.map(s=>``).join(""); const slot0=alumnos[0]||"";const slot1=alumnos[1]||""; container.innerHTML=`

👩‍🎓 Alumnas asignadas (máx. 2):

1.
2.
`; // Set current values setTimeout(()=>{ const sels=container.querySelectorAll("select"); if(sels[0]&&slot0) sels[0].value=slot0; if(sels[1]&&slot1) sels[1].value=slot1; },50); } function saveGrade(sid,mid,grade){ if(!grade) return; db.ref("pedagogia/calificaciones/"+sid+"/"+mid+"/grade").set(grade); toast("✅ Calificación guardada"); } function saveFeedback(sid,mid,fb){ if(!fb.trim()) return; db.ref("pedagogia/calificaciones/"+sid+"/"+mid+"/feedback").set(fb); toast("💬 Retroalimentación guardada"); } function deleteModContent(sid,mid){ if(!confirm("¿Borrar el contenido de este módulo en esta sesión? Se restablece la plantilla base.")) return; const base=BASE_MOD_DATA[mid]; if(base) db.ref("pedagogia/s/"+sid+"/mods/"+mid).set({...base,url:"",subBlocks:[]}); toast("🗑 Módulo restablecido"); loadTeacherDashboard(); } function resetSession(sid){ if(!confirm("¿Restablecer toda esta sesión de fábrica? Se perderá todo el progreso del equipo.")) return; db.ref("pedagogia/s/"+sid).remove(); db.ref("pedagogia/calificaciones/"+sid).remove(); toast("↺ Sesión restablecida"); loadTeacherDashboard(); } function deleteSession(sid){ if(!confirm("¿ELIMINAR esta sesión permanentemente? Se borrarán la configuración, el progreso y las calificaciones. Esta acción no se puede deshacer.")) return; db.ref("pedagogia/sesiones_config/"+sid).remove(); db.ref("pedagogia/s/"+sid).remove(); db.ref("pedagogia/calificaciones/"+sid).remove(); db.ref("pedagogia/presencia/"+sid).remove(); db.ref("pedagogia/logs/"+sid).remove(); toast("🗑 Sesión eliminada permanentemente"); loadTeacherDashboard(); loadSessionEditor(); } /* ══ URL EXTRACTOR ══ */ function extractSrc(raw){ raw=(raw||"").trim(); const srcM=raw.match(/src=["']([^"']+)["']/i); if(srcM) return srcM[1]; if(raw.startsWith("http")) return raw; return ""; } /* Convierte cualquier link de Google Drive a una URL embed que funciona sin login. Usa el viewer de Google Docs como método principal (más confiable que /preview) */ function toDriveEmbed(url){ // Extraer el file ID de cualquier formato de Drive let fileId=null; const m1=url.match(/drive\.google\.com\/file\/d\/([a-zA-Z0-9_\-]+)/); const m2=url.match(/id=([a-zA-Z0-9_\-]+)/); const m3=url.match(/open\?id=([a-zA-Z0-9_\-]+)/); if(m1) fileId=m1[1]; else if(m3) fileId=m3[1]; else if(m2) fileId=m2[1]; if(!fileId) return url; // not a Drive URL, return as-is // Método 1: Google Docs Viewer (funciona con cualquier PDF público, sin login) return `https://docs.google.com/viewer?url=https://drive.google.com/uc?id=${fileId}&embedded=true`; } function getYTId(url){ const m=url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/shorts\/)([A-Za-z0-9_\-]{11})/); return m?m[1]:null; } function isYT(url){return !!(getYTId(url)||url.includes("youtube.com/embed/")||url.includes("youtu.be/"));} function getVimeoId(url){const m=url.match(/vimeo\.com\/(?:video\/)?(\d+)/);return m?m[1]:null;} function buildVideoPlayer(raw,containerId){ const src=extractSrc(raw)||raw.trim(); if(!src) return; const container=document.getElementById(containerId); if(!container) return; if(isYT(src)){ const ytId=getYTId(src)||(src.match(/embed\/([A-Za-z0-9_\-]{11})/)||[])[1]; const thumb=ytId?`https://img.youtube.com/vi/${ytId}/hqdefault.jpg`:""; const ytUrl=ytId?`https://www.youtube.com/watch?v=${ytId}`:src; container.innerHTML=`
${thumb?`Video thumbnail`:`
▶️
`}
Ver en YouTube
ℹ️ YouTube no permite reproducir videos dentro de otras páginas. Haz clic para ver en YouTube.
`; } else { let embedSrc=src; const vid=getVimeoId(src); if(vid) embedSrc=`https://player.vimeo.com/video/${vid}`; else if(src.includes("drive.google.com")||src.includes("docs.google.com")){ // For Drive videos: use direct download link (works better than /preview for video) const driveM=src.match(/drive\.google\.com\/file\/d\/([a-zA-Z0-9_\-]+)/); if(driveM) embedSrc=`https://drive.google.com/file/d/${driveM[1]}/preview`; } container.innerHTML=`
`; } } /* ══ FONDO ANIMADO (canvas — formas por diseño) ══ */ (()=>{ const cv=document.getElementById("bubble-canvas"),ctx=cv.getContext("2d"); let W,H,particles=[]; const rs=()=>{W=cv.width=innerWidth;H=cv.height=innerHeight;}; addEventListener("resize",rs);rs(); // Shape configs per design const DESIGNS_SHAPES={ default: {colors:["#FFBCCA","#D4B8FF","#B8DCFF","#B8F0D8"],shape:"circle",count:22}, cocina: {colors:["#FF6B35","#F7C948","#FF9A76","#FFD4B8"],shape:"pizza", count:15}, musica: {colors:["#7C3AED","#EC4899","#D4B8FF","#FFBCCA"],shape:"note", count:18}, arte: {colors:["#E11D48","#7C3AED","#F43F5E","#A855F7"],shape:"star", count:16}, psicologia:{colors:["#0891B2","#6366F1","#06B6D4","#818CF8"],shape:"diamond",count:20}, baile: {colors:["#DB2777","#F97316","#EC4899","#FB923C"],shape:"heart", count:18}, }; let currentShape="circle", currentColors=DESIGNS_SHAPES.default.colors; function newParticle(colors){ return {x:Math.random()*W,y:H+Math.random()*200,r:14+Math.random()*44, vx:(Math.random()-.5)*.35,vy:-(0.25+Math.random()*.65), c:colors[~~(Math.random()*colors.length)], a:.07+Math.random()*.12,rot:Math.random()*Math.PI*2, rs:(Math.random()-.5)*.02,w:Math.random()*Math.PI*2,ws:.007+Math.random()*.01}; } for(let i=0;i<22;i++) particles.push(newParticle(currentColors)); function drawShape(shape,x,y,r,rot,color,alpha){ ctx.save();ctx.globalAlpha=alpha;ctx.fillStyle=color; ctx.translate(x,y);ctx.rotate(rot); switch(shape){ case "circle": ctx.beginPath();ctx.arc(0,0,r,0,Math.PI*2);ctx.fill(); // Shine ctx.globalAlpha=alpha*.4;ctx.fillStyle="white"; ctx.beginPath();ctx.arc(-r*.25,-r*.25,r*.2,0,Math.PI*2);ctx.fill(); break; case "pizza": ctx.beginPath();ctx.moveTo(0,0);ctx.arc(0,0,r,-Math.PI*.15,Math.PI*.85);ctx.closePath();ctx.fill(); break; case "note": ctx.beginPath();ctx.ellipse(0,r*.3,r*.35,r*.25,0,0,Math.PI*2);ctx.fill(); ctx.fillRect(r*.33-r*.07,-r*.7,r*.14,r); break; case "star": ctx.beginPath(); for(let i=0;i<5;i++){ const a=i*Math.PI*.4-Math.PI/2,ai=a+Math.PI*.2; i===0?ctx.moveTo(Math.cos(a)*r,Math.sin(a)*r):ctx.lineTo(Math.cos(a)*r,Math.sin(a)*r); ctx.lineTo(Math.cos(ai)*r*.42,Math.sin(ai)*r*.42); } ctx.closePath();ctx.fill(); break; case "diamond": ctx.beginPath();ctx.moveTo(0,-r);ctx.lineTo(r*.6,0);ctx.lineTo(0,r);ctx.lineTo(-r*.6,0);ctx.closePath();ctx.fill(); break; case "heart": ctx.beginPath();ctx.moveTo(0,r*.3); ctx.bezierCurveTo(r,r*.1,r,-r*.5,0,-r*.2); ctx.bezierCurveTo(-r,-r*.5,-r,r*.1,0,r*.3); ctx.closePath();ctx.fill(); break; } ctx.restore(); } // Expose update function for applyDesign window.updateCanvasDesign=function(designId){ const cfg=DESIGNS_SHAPES[designId]||DESIGNS_SHAPES.default; currentShape=cfg.shape;currentColors=cfg.colors; particles=[];for(let i=0;i{ p.w+=p.ws;p.rot+=p.rs; p.x+=p.vx+Math.sin(p.w)*.25;p.y+=p.vy; drawShape(currentShape,p.x,p.y,p.r,p.rot,p.c,p.a); if(p.y+p.r<-30){particles.splice(i,1);particles.push(newParticle(currentColors));} }); requestAnimationFrame(dr); })(); })(); /* ══ SONIDO ══ */ function playSound(){try{const a=new(window.AudioContext||window.webkitAudioContext)();[523.25,659.25,783.99,1046.5].forEach((f,i)=>{const o=a.createOscillator(),g=a.createGain();o.connect(g);g.connect(a.destination);o.frequency.value=f;o.type="sine";const t=a.currentTime+i*.18;g.gain.setValueAtTime(0,t);g.gain.linearRampToValueAtTime(.2,t+.04);g.gain.exponentialRampToValueAtTime(.001,t+.45);o.start(t);o.stop(t+.5);});}catch(e){}} /* ══ ARRANQUE ══ */ window.addEventListener("load",()=>initLobby()); /* ══ URL EXTRACTOR ══ YouTube: thumbnail clickable (no embed por política de YouTube) Vimeo/Drive/otros: embed normal /* ══ SECCIONES ══ */ function showSec(s,btn){ document.querySelectorAll(".cpill").forEach(b=>b.classList.remove("active"));btn.classList.add("active"); document.querySelectorAll(".section-mooc,.section-micro,.section-blearning").forEach(el=>el.classList.remove("active")); const m={mooc:".section-mooc",micro:".section-micro",blearning:".section-blearning"}; document.querySelector(m[s]).classList.add("active"); } function swCtab(el){document.querySelectorAll(".ctab").forEach(b=>b.classList.remove("active"));el.classList.add("active");} function mNav(el){document.querySelectorAll(".mni").forEach(b=>b.classList.remove("active"));el.classList.add("active");} /* ══ MOOC — MOSTRAR MÓDULO ══ */ function showMod(id){ document.querySelectorAll(".mod-item").forEach(m=>m.classList.remove("active")); const sideEl=document.getElementById(id); if(sideEl) sideEl.classList.add("active"); currentModId=id; const d=modData[id]; if(!d){toast("⚠️ Sin datos");return;} const tColors={intro:"#D4734A",video:"#3A7BC8",lectura:"#2A9D6A",foro:"#B08A00",actividad:"#8A4AC8",quiz:"#D4455A"}; const tBg={intro:"rgba(255,212,184,.3)",video:"rgba(184,220,255,.3)",lectura:"rgba(184,240,216,.3)",foro:"rgba(255,240,176,.3)",actividad:"rgba(212,184,255,.3)",quiz:"rgba(255,188,202,.3)"}; const tLabel={intro:"Introducción",video:"Video",lectura:"Lectura",foro:"Foro",actividad:"Actividad",quiz:"Quiz"}; const allMods=Array.from(document.querySelectorAll(".mod-item")); const idx=allMods.findIndex(m=>m.id===id); const prevId=idx>0?allMods[idx-1].id:null; const nextId=idx

🎯 ¿Qué debe hacer el alumno para completar este módulo?

${compOptions.map(o=>``).join("")} `; /* —— Contenido principal por tipo —— */ let mainContent=""; if(d.type==="intro"){ // Initialize objectives if not set — save to modData immediately if(!d.objectives || d.objectives.length===0){ d.objectives=["Objetivo 1: Conocer los fundamentos de la pedagogía digital","Objetivo 2: Identificar tipos de EVA","Objetivo 3: Diseñar una secuencia básica"]; modData[id].objectives=d.objectives; // Don't autosave yet — wait for user to edit } const objHtml=d.objectives.map((obj,i)=> `
  • ${obj}
  • ` ).join(""); mainContent=`

    ${d.title}

    ${d.desc}

      ${objHtml}
    `; } else if(d.type==="video"){ mainContent=`

    ${d.title}

    ${d.desc}

    ${d.url?"
    ":""}
    💡 YouTube: pega el link normal (abre en YouTube) · Vimeo/Drive: se reproduce aquí directamente
    ⚠️ Si usas Google Drive: el video debe estar en "Compartir → Cualquier persona con el enlace"
    `; } else if(d.type==="lectura"){ mainContent=`

    ${d.title}

    ${d.desc}

    Cómo compartir un PDF de Google Drive:
    1. Abre el archivo en Drive → clic en "Compartir"
    2. Acceso general → "Cualquier persona con el enlace" → Lector
    3. Clic en "Copiar enlace" → pega aquí abajo
    También puedes pegar links directos a PDFs (.pdf)
    ${d.url?``:""}

    📝 Mis apuntes:

    `; } else if(d.type==="foro"){ mainContent=`

    ${d.title}

    ${d.desc}

    C
    Cuicalli (Profesora)
    Hace 2 días
    ¡Bienvenidas! Presenten su nombre y cuéntennos: ¿por qué eligieron estudiar pedagogía? 🌸
    `; } else if(d.type==="actividad"){ mainContent=`

    ${d.title}

    ${d.desc}

    ${d.url?`
    `:""}
    💡 EducaPlay: abre tu actividad → Integrar → copia el código iframe completo
    `; } else if(d.type==="quiz"){ const qd=d.quiz; const qHtml=qd.preguntas.map((p,qi)=>`
    ❓ ${p.q}
    ${p.opts.map((o,oi)=>``).join("")}
    `).join(""); mainContent=`

    ${d.title}

    ${d.desc}

    ${qHtml}

    🔧 Editor de preguntas — selecciona el radio ✓ para marcar la respuesta correcta

    ${qd.preguntas.map((p,qi)=>`
    P${qi+1}
    ${p.opts.map((o,oi)=>`
    `).join("")}
    `).join("")}
    `; } /* —— Sub-bloques adicionales —— */ const subHtml=(d.subBlocks||[]).map((sb,si)=>renderSubBlock(sb,id,si)).join(""); /* —— Render completo del panel —— */ const panel=document.getElementById("mooc-panel"); panel.innerHTML=`
    ${tLabel[d.type]||d.type}
    ${tLabel[d.type]} · ${d.title}
    ${mainContent}
    ${subHtml}
    ${compHtml}
    🎯 Completa: ${getCompLabel(d.compTrigger)}
    ${nextId?``:"🎉 ¡Fin del módulo!"}
    `; /* Cargar video si existe */ if(d.type==="video"&&d.url){ setTimeout(()=>buildVideoPlayer(d.url,`vid-container-${id}`),50); } } function getCompLabel(t){ const m={ninguno:"Automático",ver_video:"Ver el video",foro:"Participar en el foro",quiz:"Responder el quiz",actividad:"Completar la actividad",apuntes:"Escribir apuntes"}; return m[t]||t; } /* ══ EDICIÓN DE OBJETIVOS ══ */ const _objSaveTimers={}; function debouncedSaveObjective(modId,idx,el){ // Update modData immediately so the data is current if(modData[modId]){ if(!modData[modId].objectives) modData[modId].objectives=[]; modData[modId].objectives[idx]=el.textContent.trim()||("Objetivo "+(idx+1)); } // Debounce the Firebase write clearTimeout(_objSaveTimers["obj-"+modId]); _objSaveTimers["obj-"+modId]=setTimeout(()=>flushObjectives(modId),800); } function saveObjective(modId,idx,text){ if(!modData[modId]) return; // Capture FULL list from DOM const objList=document.getElementById("obj-list-"+modId); if(objList){ modData[modId].objectives=Array.from(objList.querySelectorAll("li")) .map(li=>li.textContent.trim()).filter(t=>t); } else { if(!modData[modId].objectives) modData[modId].objectives=[]; modData[modId].objectives[idx]=text.trim()||("Objetivo "+(idx+1)); } flushObjectives(modId); } function flushObjectives(modId){ // Capture full DOM list one more time before writing const objList=document.getElementById("obj-list-"+modId); if(objList&&modData[modId]){ const items=Array.from(objList.querySelectorAll("li")).map(li=>li.textContent.trim()).filter(t=>t); if(items.length>0) modData[modId].objectives=items; } triggerAutoSave(); } function addObjective(modId){ if(!modData[modId]) return; if(!modData[modId].objectives) modData[modId].objectives=[]; const newIdx=modData[modId].objectives.length; modData[modId].objectives.push("Nuevo objetivo"); const list=document.getElementById("obj-list-"+modId); if(list){ const li=document.createElement("li"); li.contentEditable="true"; li.id="obj-"+modId+"-"+newIdx; li.textContent="Nuevo objetivo"; li.style.cssText="outline:none;padding:2px 4px;border-radius:4px;transition:background .2s"; li.addEventListener("focus",()=>li.style.background="rgba(255,143,163,.08)"); li.addEventListener("blur",()=>{li.style.background="";flushObjectives(modId);}); li.addEventListener("input",()=>{ if(modData[modId]&&modData[modId].objectives) modData[modId].objectives[newIdx]=li.textContent.trim(); clearTimeout(_objSaveTimers["obj-"+modId]); _objSaveTimers["obj-"+modId]=setTimeout(()=>flushObjectives(modId),800); }); li.addEventListener("keydown",e=>{if(e.key==="Enter"){e.preventDefault();li.blur();}}); list.appendChild(li); li.focus(); // Select all text const range=document.createRange();range.selectNodeContents(li); const sel=window.getSelection();sel.removeAllRanges();sel.addRange(range); } triggerAutoSave(); } function saveModField(modId,field,text){ if(!modData[modId]) return; modData[modId][field]=text.trim(); triggerAutoSave(); } function setCompTrigger(modId,val,btn){ if(modData[modId]) modData[modId].compTrigger=val; document.querySelectorAll(".comp-opt").forEach(b=>b.classList.remove("selected")); btn.classList.add("selected"); // Si es ninguno, marcar completado inmediatamente if(val==="ninguno") markDone(modId); } /* ══ CARGAR RECURSOS ══ */ function loadModVid(id){ const inp=document.getElementById("panel-vurl"); if(!inp||!inp.value.trim()){toast("⚠️ Ingresa una URL");return;} const url=inp.value.trim(); if(modData[id]) modData[id].url=url; buildVideoPlayer(url,`vid-container-${id}`); if(modData[id]?.compTrigger==="ver_video") markDone(id); triggerAutoSave(); toast("🎬 Video cargado"); } function loadModPDF(id){ const inp=document.getElementById("panel-purl"); if(!inp||!inp.value.trim()){toast("⚠️ Ingresa una URL");return;} const raw=inp.value.trim(); // Convert Drive URL to embed-friendly format const isDrive=raw.includes("drive.google.com")||raw.includes("docs.google.com"); const embedUrl = isDrive ? toDriveEmbed(raw) : (extractSrc(raw)||raw); if(modData[id]) modData[id].url=embedUrl; modData[id]._originalUrl=raw; // keep original for the "open in Drive" link const cont=document.getElementById(`pdf-container-${id}`); const errBox=document.getElementById("pdf-access-error-"+id); if(errBox) errBox.style.display="none"; if(cont){ cont.style.display="block"; cont.innerHTML=``; } triggerAutoSave(); toast("📄 Cargando PDF..."); } function handlePDFLoad(iframe, modId){ // The Google Docs Viewer always "loads" successfully (it shows its own error page if needed) // Hide any previous error const errBox=document.getElementById("pdf-access-error-"+modId); if(errBox) errBox.style.display="none"; } function checkIframeAccess(iframe, modId){ // After iframe loads, check if it's showing a Drive login page // We can't read iframe content cross-origin, but we can check the URL try{ const src=iframe.src||""; // If Drive redirected to accounts.google.com, it needs login if(src.includes("accounts.google.com")||src.includes("ServiceLogin")){ showDriveError(modId); } }catch(e){ // Cross-origin — assume it loaded fine } } function showDriveError(modId){ const errBox=document.getElementById("pdf-access-error-"+modId); const cont=document.getElementById("pdf-container-"+modId); if(errBox) errBox.style.display="block"; if(cont) cont.style.display="none"; } function loadModAct(id){ const inp=document.getElementById("panel-aurl"); if(!inp||!inp.value.trim()) return; const src=extractSrc(inp.value.trim())||inp.value.trim(); if(!src){toast("⚠️ URL no reconocida");return;} if(modData[id]) modData[id].url=src; const cont=document.getElementById(`act-container-${id}`); if(cont) cont.innerHTML=``; toast("🎮 Actividad cargada"); } function loadMicroVid(){ const raw=document.getElementById("micro-vurl").value; buildVideoPlayer(raw,"micro-vid-area"); document.getElementById("micro-vph")?.remove(); } function liveIF(id,raw){ const el=document.getElementById(id);if(!el)return; const src=extractSrc(raw)||raw.trim(); if(!src){el.style.display="none";return;} el.src=isYT(src)?raw:src; if(isYT(src)){el.style.display="none";toast("ℹ️ YouTube: pega la URL en el campo de video y usa el botón Cargar");} else{el.style.display="block";el.style.height="250px";} } /* ══ QUIZ ══ */ function highlightCorrectOpt(qi){ for(let oi=0;oi<8;oi++){ const radio=document.getElementById(`qer-${qi}-${oi}`); if(!radio) break; const row=radio.closest("div"); if(!row) continue; const checked=radio.checked; row.style.background=checked?"rgba(77,217,172,.08)":"white"; row.style.borderColor=checked?"#4DD9AC":"rgba(212,184,255,.2)"; const lbl=row.querySelector("label"); if(lbl){lbl.textContent=checked?"✓ Correcta":"marcar ✓";lbl.style.color=checked?"#2A9D6A":"var(--text-soft)";} } } function selQ(qi,oi,correcta){ /* Only clear selected/wrong (not correct class since we show correct initially) */ for(let i=0;i<8;i++){const b=document.getElementById(`qo-${qi}-${i}`);if(!b)break;b.classList.remove("selected","wrong");} const btn=document.getElementById(`qo-${qi}-${oi}`);if(btn)btn.classList.add("selected"); quizAnswers[qi]=oi; } function checkQ(qi,correcta,modId){ if(quizAnswers[qi]===undefined){toast("⚠️ Selecciona una opción");return;} const sel=quizAnswers[qi]; const cb=document.getElementById(`qo-${qi}-${correcta}`);if(cb)cb.classList.add("correct"); if(sel!==correcta){const wb=document.getElementById(`qo-${qi}-${sel}`);if(wb)wb.classList.add("wrong");} const fb=document.getElementById(`qfb-${qi}`); if(fb){fb.style.display="block";fb.className="quiz-fb "+(sel===correcta?"ok":"fail");fb.textContent=sel===correcta?"✅ ¡Correcto!":"❌ Incorrecto. La correcta está en verde.";} if(sel===correcta&&modData[modId]?.compTrigger==="quiz") markDone(modId); } function applyQEdits(id){ const d=modData[id];if(!d||!d.quiz)return; d.quiz.preguntas.forEach((p,qi)=>{ const qi_el=document.getElementById(`qe-q-${qi}`);if(qi_el)p.q=qi_el.value; p.opts.forEach((o,oi)=>{const el=document.getElementById(`qe-o-${qi}-${oi}`);if(el)p.opts[oi]=el.value;}); const r=document.querySelector(`input[name="qec-${qi}"]:checked`);if(r)p.correcta=parseInt(r.value); }); quizAnswers={};showMod(id);toast("✅ Quiz actualizado"); } function addQuestion(id){ const d=modData[id];if(!d||!d.quiz)return; applyQEdits(id); d.quiz.preguntas.push({q:"Nueva pregunta",opts:["Opción A","Opción B","Opción C","Opción D"],correcta:0}); showMod(id);toast("➕ Pregunta agregada"); } function delQuestion(qi,id){ const d=modData[id];if(!d||!d.quiz||d.quiz.preguntas.length<=1){toast("⚠️ Mínimo 1 pregunta");return;} applyQEdits(id);d.quiz.preguntas.splice(qi,1);quizAnswers={};showMod(id);toast("🗑 Pregunta eliminada"); } /* ══ FORO ══ */ function sendForoMsg(modId){ const inp=document.getElementById("foro-new-msg");if(!inp||!inp.value.trim())return; const n=currentMember?.name||"Alumna"; const post=document.createElement("div");post.className="foro-post"; post.innerHTML=`
    ${n[0].toUpperCase()}
    ${n}
    Ahora
    ${inp.value}
    `; document.getElementById("foro-posts")?.appendChild(post); inp.value="";post.scrollIntoView({behavior:"smooth"}); toast("💬 Participación enviada"); triggerAutoSave(); logChange("Foro participó en módulo "+modId); if(modData[modId]?.compTrigger==="foro") markDone(modId); try{db.ref("pedagogia/foros/"+currentSession.id+"/"+modId+"/"+Date.now()).set({autor:n,msg:post.querySelector(".foro-tx").textContent,ts:Date.now()});}catch(e){} } function checkApuntesCompletion(modId){ if(modData[modId]?.compTrigger==="apuntes") markDone(modId); triggerAutoSave(); } /* ══ SUB-BLOQUES (contenido adicional dentro de un módulo) ══ */ function renderSubBlock(sb,modId,si){ const uid=sb.uid||("sb-"+modId+"-"+si); // Parse quiz data stored in sb const sbQuiz=sb.quiz||{pregunta:sb.desc||"Escribe tu pregunta aquí",opts:["Opción A","Opción B","Opción C","Opción D"],correcta:0}; let inner=""; if(sb.type==="video"){ inner=`
    🎬 ${sb.title}
    ${sb.url?"":" "}
    💡 YouTube abre en YouTube · Vimeo/Drive se reproduce aquí
    `; } else if(sb.type==="lectura"){ const pdfEmbed=sb.url?toDriveEmbed(sb.url):""; inner=`
    📄 ${sb.title}
    ${pdfEmbed?``:""}
    💡 Google Drive → Compartir → Cualquier persona con el enlace → copiar link
    `; } else if(sb.type==="actividad"){ inner=`
    🎮 ${sb.title}
    ${sb.url?``:""}
    💡 EducaPlay, Kahoot, Wordwall — pega el código iframe o URL
    `; } else if(sb.type==="foro"){ /* Foro como Q&A: el editor pone la pregunta, los alumnos responden */ const foroKey="pedagogia/foros_sb/"+(currentSession?.id||"s0")+"/"+uid; inner=`
    💬 ${sb.title}

    ❓ Pregunta del foro:

    ${sb.desc||"Escribe aquí la pregunta para el grupo..."}

    `; // Load existing replies async setTimeout(()=>loadForoReplies(uid),100); } else if(sb.type==="quiz"){ /* Fully editable quiz sub-block */ const opts=sbQuiz.opts||["Opción A","Opción B","Opción C","Opción D"]; inner=`
    🧩 ${sb.title}
    ❓ ${sbQuiz.pregunta}
    ${opts.map((o,oi)=>``).join("")}

    ✏️ Editar pregunta y respuestas — marca ✓ la correcta

    ${opts.map((o,oi)=>`
    `).join("")}
    `; } else { // texto/nota — inline rich text editor const isRich=sb.isRichText; inner=`
    ${isRich?(sb.desc||""):(sb.desc||"Escribe tu nota...")}
    `; } return `
    ${inner}
    `; } let mgScores={}; function checkMG(uid,answer,correct){ if(!mgScores[uid]) mgScores[uid]={total:0,correct:0}; mgScores[uid].total++; const fb=document.getElementById("mg-fb-"+uid); const sc=document.getElementById("mg-score-"+uid); if(answer===correct){mgScores[uid].correct++;if(fb){fb.style.display="block";fb.style.background="rgba(77,217,172,.15)";fb.style.color="#2A9D6A";fb.textContent="✅ ¡Correcto!";}} else{if(fb){fb.style.display="block";fb.style.background="rgba(255,136,119,.15)";fb.style.color="#D4455A";fb.textContent="❌ Incorrecto.";}} if(sc) sc.textContent=mgScores[uid].correct+" / "+mgScores[uid].total+" correctas"; } /* ══ QUIZ SUB-BLOCK ══ */ let sbQuizAnswers={}; function selectSBQ(uid,oi,correcta){ for(let i=0;i<8;i++){const b=document.getElementById(`sbqo-${uid}-${i}`);if(!b)break;b.classList.remove("selected","wrong","correct");} const btn=document.getElementById(`sbqo-${uid}-${oi}`);if(btn)btn.classList.add("selected"); sbQuizAnswers[uid]=oi; } function checkSBQ(uid,correcta,modId){ if(sbQuizAnswers[uid]===undefined){toast("⚠️ Selecciona una opción");return;} const sel=sbQuizAnswers[uid]; const cb=document.getElementById(`sbqo-${uid}-${correcta}`);if(cb)cb.classList.add("correct"); if(sel!==correcta){const wb=document.getElementById(`sbqo-${uid}-${sel}`);if(wb)wb.classList.add("wrong");} const fb=document.getElementById("sbqfb-"+uid); if(fb){fb.style.display="block";fb.className="quiz-fb "+(sel===correcta?"ok":"fail");fb.textContent=sel===correcta?"✅ ¡Correcto!":"❌ Incorrecto. La correcta está marcada en verde.";} if(sel===correcta&&modData[modId]?.compTrigger==="quiz") markDone(modId); } function highlightSBQOpt(uid){ for(let oi=0;oi<8;oi++){ const radio=document.getElementById(`sbqr-${uid}-${oi}`);if(!radio)break; const row=document.getElementById(`sbqrow-${uid}-${oi}`);const lbl=row?.querySelector("label"); const checked=radio.checked; if(row){row.style.borderColor=checked?"#4DD9AC":"#EDD8F0";row.style.background=checked?"rgba(77,217,172,.08)":"#FFFBF0";} if(lbl){lbl.textContent=checked?"✓ Correcta":"marcar ✓";lbl.style.color=checked?"#2A9D6A":"#A99EBD";} } } function applySBQuiz(uid,modId){ const pregunta=document.getElementById("sbqtext-"+uid)?.value||"Pregunta"; const opts=[];let correcta=0; for(let oi=0;oi<4;oi++){ const opt=document.getElementById(`sbqopt-${uid}-${oi}`); opts.push(opt?opt.value:"Opción "+(oi+1)); if(document.getElementById(`sbqr-${uid}-${oi}`)?.checked) correcta=oi; } // Update modData subBlock if(modId&&modData[modId]?.subBlocks){ const sb=modData[modId].subBlocks.find(s=>s.uid===uid); if(sb){sb.quiz={pregunta,opts,correcta};sb.desc=pregunta;} } // Update display const qText=document.querySelector(`#sbqq-${uid} .quiz-q-text`); if(qText) qText.textContent="❓ "+pregunta; opts.forEach((o,oi)=>{const btn=document.getElementById(`sbqo-${uid}-${oi}`);if(btn)btn.textContent=o;}); sbQuizAnswers[uid]=undefined; document.querySelectorAll(`#sbqq-${uid} .quiz-opt`).forEach(b=>b.classList.remove("selected","correct","wrong")); const fb=document.getElementById("sbqfb-"+uid);if(fb)fb.style.display="none"; triggerAutoSave(); toast("✅ Quiz actualizado"); logChange("Quiz editado: "+pregunta); } /* ══ FORO Q&A SUB-BLOCK ══ */ function saveForoQuestion(uid,modId,text){ if(modId&&modData[modId]?.subBlocks){ const sb=modData[modId].subBlocks.find(s=>s.uid===uid); if(sb){sb.desc=text;triggerAutoSave();} } logChange("Foro editado: "+text.substring(0,40)); } function loadSubVid(uid,inputId){ const inp=document.getElementById(inputId);if(!inp||!inp.value.trim())return; buildVideoPlayer(inp.value.trim(),"sv-"+uid); // Save URL to subBlock if(currentModId&&modData[currentModId]?.subBlocks){const sb=modData[currentModId].subBlocks.find(s=>s.uid===uid);if(sb)sb.url=inp.value.trim();} triggerAutoSave();toast("🎬 Video cargado"); } function loadSubPDF(uid,inputId){ const inp=document.getElementById(inputId);if(!inp||!inp.value.trim())return; const raw=inp.value.trim(); const embedUrl=toDriveEmbed(raw); const cont=document.getElementById("sp-"+uid); if(cont) cont.innerHTML=``; if(currentModId&&modData[currentModId]?.subBlocks){const sb=modData[currentModId].subBlocks.find(s=>s.uid===uid);if(sb)sb.url=raw;} triggerAutoSave();toast("📄 PDF cargado"); } function loadSubAct(uid,inputId){ const inp=document.getElementById(inputId);if(!inp||!inp.value.trim())return; const src=extractSrc(inp.value.trim())||inp.value.trim(); const cont=document.getElementById("sa-"+uid); if(cont) cont.innerHTML=``; if(currentModId&&modData[currentModId]?.subBlocks){const sb=modData[currentModId].subBlocks.find(s=>s.uid===uid);if(sb)sb.url=src;} triggerAutoSave();toast("🎮 Actividad cargada"); } /* ══ LOG DE CAMBIOS ══ */ function logChange(action){ if(!currentSession||!currentMember) return; try{ db.ref("pedagogia/logs/"+currentSession.id).push({ action, by:currentMember.name, session:currentSession.nombre, ts:Date.now() }); }catch(e){} } function openSubModal(containerId){ subModalTarget=containerId; document.getElementById("mmod-grid").style.display="grid"; document.getElementById("mmod-form").style.display="none"; document.getElementById("mm-ok").style.display="none"; addModType=null; ["mm-title","mm-url","mm-desc"].forEach(id=>{const el=document.getElementById(id);if(el)el.value="";}); oM("modal-mod"); } function setMT(t){ if(t==="texto"){ cM("modal-mod"); insertInlineNote(currentModId); return; } addModType=t; document.getElementById("mmod-grid").style.display="none"; document.getElementById("mmod-form").style.display="block"; document.getElementById("mm-ok").style.display="inline-block"; const needsUrl=["video","lectura","actividad","juego"]; document.getElementById("mm-url-wrap").style.display=needsUrl.includes(t)||t==="foro"?"block":"none"; const L={video:"URL del video (YouTube, Vimeo, Drive...)",lectura:"URL del PDF",actividad:"URL o código iframe (EducaPlay, Kahoot...)",foro:"URL del foro externo (opcional)"}; const H={video:"💡 YouTube thumbnail + link · Vimeo/Drive embed directo",lectura:"💡 Google Drive PDF link",actividad:"💡 EducaPlay: Integrar → copiar código iframe",foro:"💡 Padlet, Mentimeter, etc. (opcional)"}; document.getElementById("mm-url-lbl").textContent=L[t]||"URL"; document.getElementById("mm-url-help").textContent=H[t]||""; } function confirmSubBlock(){ if(!addModType) return; const title=document.getElementById("mm-title").value.trim()||"Nuevo contenido"; const rawUrl=document.getElementById("mm-url").value.trim(); const desc=document.getElementById("mm-desc").value.trim(); const uid="sb-"+(currentSession?.id||"s0")+"-"+Date.now(); const url=rawUrl?extractSrc(rawUrl)||rawUrl:""; // For quiz, store initial quiz data const quiz=addModType==="quiz"?{pregunta:desc||"Escribe tu pregunta aquí",opts:["Opción A","Opción B","Opción C","Opción D"],correcta:0}:null; const sb={uid,type:addModType,title,url,desc,quiz}; if(currentModId&&modData[currentModId]){ if(!modData[currentModId].subBlocks) modData[currentModId].subBlocks=[]; modData[currentModId].subBlocks.push(sb); } const container=document.getElementById(subModalTarget); if(container){ const div=document.createElement("div"); div.innerHTML=renderSubBlock(sb,currentModId,Date.now()); container.appendChild(div.firstElementChild); if(url&&addModType==="video") setTimeout(()=>buildVideoPlayer(url,"sv-"+uid),80); if(url&&addModType==="lectura") setTimeout(()=>{ const cont=document.getElementById("sp-"+uid); if(cont) cont.innerHTML=``; },80); } cM("modal-mod"); triggerAutoSave(); // sync to all collaborators logChange("Contenido agregado: "+title+" ("+addModType+")"); toast("✨ Contenido agregado"); } function delSubBlock(uid,modId){ const el=document.getElementById(uid); if(el){el.style.opacity="0";el.style.transition="opacity .3s";setTimeout(()=>el.remove(),300);} if(modId&&modData[modId]?.subBlocks){modData[modId].subBlocks=modData[modId].subBlocks.filter(s=>s.uid!==uid);} toast("🗑 Eliminado"); } /* ══ NUEVO MÓDULO PRINCIPAL ══ */ function confirmNewMod(){ const name=document.getElementById("nm-name").value.trim()||"Nuevo módulo"; const type=document.getElementById("nm-type").value; const desc=document.getElementById("nm-desc").value.trim()||"Descripción del módulo."; const sid=currentSession?.id||"s0"; const uid="mi-"+sid+"-"+Date.now(); // session-scoped to prevent cross-session bleed const tagClass={intro:"tag-intro",video:"tag-video",lectura:"tag-lectura",foro:"tag-foro",actividad:"tag-actividad",quiz:"tag-quiz",texto:"tag-intro"}; modData[uid]={type,title:name,url:"",desc,compTrigger:"ninguno",subBlocks:[],quiz:type==="quiz"?{preguntas:[{q:"Nueva pregunta",opts:["Opción A","Opción B","Opción C","Opción D"],correcta:0}]}:null}; const btn=document.createElement("div");btn.className="mod-item";btn.id=uid; btn.innerHTML=`
    ${TYPE_ICONS[type]||"📋"}${name}${type}`; btn.onclick=(e)=>{if(!e.target.classList.contains("del-btn")&&!e.target.closest(".del-btn"))showMod(uid);}; const units=document.querySelectorAll(".unit-group"); (units[units.length-1]||document.getElementById("mooc-units")).appendChild(btn); // Wire up del-btn after appending (can't use onclick in innerHTML for nested buttons in button) document.getElementById("dbtn-"+uid)?.addEventListener("click",e=>{e.stopPropagation();delMod(uid);}); cM("modal-newmod"); updateProgress(); triggerAutoSave(); logChange("Módulo creado: "+name+" ("+type+")"); toast("✨ Módulo creado"); // Auto-navigate to the new module immediately showMod(uid); } function confirmUnit(){ const name=document.getElementById("unit-name").value.trim()||"Nueva Unidad"; const sid=currentSession?.id||"s0"; const uid="ug-"+sid+"-"+Date.now(); // session-scoped const div=document.createElement("div");div.className="unit-group";div.id=uid; div.innerHTML=`
    ${name.toUpperCase()}
    `; document.getElementById("mooc-units").appendChild(div); document.getElementById("dbtn-"+uid)?.addEventListener("click",()=>delEl(uid)); cM("modal-unit"); triggerAutoSave(); logChange("Unidad creada: "+name); toast("📁 Unidad creada"); } function delMod(id){ const el=document.getElementById(id); if(el){el.style.opacity="0";el.style.transition="opacity .3s";setTimeout(()=>{el.remove();updateProgress();triggerAutoSave();logChange("Módulo eliminado: "+id);},320);} if(currentModId===id){document.getElementById("mooc-panel").innerHTML=`
    👈

    Selecciona un módulo

    `;currentModId=null;} if(modData[id]) delete modData[id]; toast("Módulo eliminado"); } function delLastMod(){ const mods=document.querySelectorAll(".mod-item"); if(mods.length>0) delMod(mods[mods.length-1].id); else toast("⚠️ No hay módulos para eliminar"); } function markDone(id){ const el=document.getElementById(id);if(!el)return; const dot=el.querySelector(".mod-dot"); if(dot&&!dot.classList.contains("done")){dot.className="mod-dot done";dot.textContent="✓";updateProgress();toast("✅ ¡Módulo completado! 🎉");triggerAutoSave();} } function updateProgress(){ const all=document.querySelectorAll(".mod-item").length; const done=document.querySelectorAll(".mod-dot.done").length; const pct=all>0?Math.round(done/all*100):0; const pf=document.getElementById("mooc-prog");if(pf)pf.style.width=pct+"%"; const pl=document.getElementById("mooc-prog-lbl");if(pl)pl.textContent=done+" de "+all+" completados"; } /* ══ MICRO ══ */ function openLesson(name){document.getElementById("micro-ltitle").textContent="📖 Lección: "+name;document.getElementById("micro-lp").scrollIntoView({behavior:"smooth"});} function confirmNode(){ const name=document.getElementById("nd-name").value.trim();const emoji=document.getElementById("nd-emoji").value||"📖";if(!name)return; const path=document.getElementById("micro-path"); const conn=document.createElement("div");conn.className="path-conn"; const btn=document.createElement("button");btn.className="path-node locked";btn.textContent=emoji;btn.id="pn-"+Date.now();btn.onclick=()=>openLesson(name); path.appendChild(conn);path.appendChild(btn);cM("modal-node");toast("⚡ Lección agregada"); } function delLastNode(){ const nodes=document.querySelectorAll("#micro-path .path-node"); if(nodes.length<=1){toast("⚠️ Mínimo 1 lección");return;} const last=nodes[nodes.length-1];const prev=last.previousElementSibling; if(prev&&prev.classList.contains("path-conn"))prev.remove();last.remove();toast("🗑 Lección eliminada"); } /* ══ B-LEARNING ══ */ function confirmTask(){ const title=document.getElementById("tk-title").value.trim()||"Nueva tarea"; const desc=document.getElementById("tk-desc").value||"Instrucciones."; const rawUrl=document.getElementById("tk-url").value.trim(); const date=document.getElementById("tk-date").value; const url=rawUrl?extractSrc(rawUrl)||rawUrl:""; const uid="tk"+Date.now(); const d=document.createElement("div");d.className="assign-card";d.id=uid; let iframeHtml=""; if(url){ if(isYT(url)){iframeHtml=`
    ▶️ Ver video en YouTube
    `;} else{iframeHtml=``;} } d.innerHTML=`
    📋
    ${title}
    Publicada · ${date?"Entrega: "+date:"Sin fecha"}

    ${desc}

    ${iframeHtml}
    `; document.getElementById("bl-extra").appendChild(d); cM("modal-task");toast("📋 Tarea agregada"); db.ref("cursos/blearning/tareas").push({titulo:title,desc,url,date,autor:currentUser?.nombre,ts:Date.now()}); } function delLastTask(){ const extra=document.getElementById("bl-extra");const cards=extra.querySelectorAll(".assign-card"); if(cards.length>0){cards[cards.length-1].remove();toast("🗑 Eliminada");}else toast("⚠️ No hay tareas adicionales"); } function addUpcoming(){ const d=document.createElement("div");d.className="up-item";d.innerHTML=`
    Nueva fecha...`; document.getElementById("upcoming-list").appendChild(d);d.querySelector("span").focus(); } function delLastUpcoming(){ const list=document.getElementById("upcoming-list");const items=list.querySelectorAll(".up-item"); if(items.length>1){items[items.length-1].remove();toast("🗑 Eliminada");}else toast("⚠️ Mínimo 1 fecha"); } /* ══ COMPAÑERAS ══ */ function buildClassmates(){ const el=document.getElementById("classmates-list"); if(!el) return; el.innerHTML=""; if(currentSession){ db.ref("pedagogia/presencia/"+currentSession.id).once("value").then(snap=>{ const members=Object.values(snap.val()||{}).filter(m=>m?.name); if(members.length===0){el.innerHTML=`Nadie más conectado aún`;return;} members.forEach(m=>{ const av=document.createElement("div");av.className="cm-av";av.title=m.name; av.textContent=m.name[0].toUpperCase(); if(m.online) av.style.boxShadow="0 0 0 2px #4DD9AC"; el.appendChild(av); }); }); } } /* ══ DELETE ══ */ function delEl(id){ const el=document.getElementById(id); if(el){ el.style.opacity="0";el.style.transform="scale(.95)";el.style.transition="opacity .25s,transform .25s"; setTimeout(()=>{el.remove();triggerAutoSave();logChange("Unidad/elemento eliminado: "+id);},260); } toast("✕ Eliminado"); } /* ══ VISTA PREVIA NAVEGABLE ══ */ let previewMods=[], previewIdx=0; function openPreview(section){ // Build the preview HTML content const previewData={ section, modData:JSON.parse(JSON.stringify(modData)), sidebarState:buildSavePayload().sidebarState, courseName:document.getElementById("course-name")?.textContent||"", design:document.body.dataset.design||"default", theme:document.body.className||"theme-1", previewMods:Array.from(document.querySelectorAll(".mod-item")).map(m=>m.id), progreso:{}, }; document.querySelectorAll(".mod-item").forEach(m=>{ previewData.progreso[m.id]=m.querySelector(".mod-dot")?.classList.contains("done")?"done":"pending"; }); // Store in sessionStorage for the new window to read try{sessionStorage.setItem("eva_preview",JSON.stringify(previewData));}catch(e){} // Open new window — full screen const pw=window.open("","eva_preview","width="+screen.width+",height="+screen.height+",top=0,left=0"); if(!pw){toast("⚠️ El navegador bloqueó la ventana. Permite popups para esta página.");return;} // Write the preview page pw.document.write(buildPreviewHTML(previewData)); pw.document.close(); toast("👁 Vista previa abierta en nueva ventana"); } function buildPreviewHTML(data){ // Get current CSS from parent page const styles=Array.from(document.styleSheets).map(s=>{try{return Array.from(s.cssRules).map(r=>r.cssText).join("\n");}catch(e){return "";}}).join("\n"); const md=data.modData||{}; const mods=data.previewMods||[]; const prog=data.progreso||{}; const courseName=data.courseName||"Pedagogía Digital"; // Build sidebar HTML let sidebarHTML=""; if(data.sidebarState?.units){ data.sidebarState.units.forEach(u=>{ sidebarHTML+=`
    ${u.name}
    `; (data.sidebarState.allMods||[]).filter(m=>m.unitId===u.id).forEach(m=>{ const isDone=prog[m.id]==="done"; const isCur=mods[0]===m.id; sidebarHTML+=`
    ${isDone?"✓":"○"}
    ${m.label}
    `; }); }); } // Build main content for first module const firstModId=mods[0]||Object.keys(md)[0]||""; const firstMod=md[firstModId]||{}; return ` 👁 Vista previa — ${courseName}
    👁 Vista previa — ${courseName}
    1 / ${mods.length}
    ${courseName}
    ${sidebarHTML}
    Black IA